Last commit 08 Sep 2011
Description: Netservices modded to read fragmented HTTP respsonse/payload from special purpose server - 180 bytes only
Revision 0:850eacf3e945, committed 08 Sep 2011
- Comitter:
- Date:
- Thu Sep 08 10:48:09 2011 +0000
- Commit message:
- revised fixed length to 178 bytes
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/api/DNSRequest.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,115 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "DNSRequest.h"
+#include "if/net/netdnsrequest.h"
+
+DNSRequest::DNSRequest() : m_pNetDnsRequest(NULL), m_pCbItem(NULL), m_pCbMeth(NULL), m_pCb(NULL)
+{
+
+}
+
+DNSRequest::~DNSRequest()
+{
+ close();
+}
+
+DNSRequestErr DNSRequest::resolve(const char* hostname)
+{
+ if(!m_pNetDnsRequest)
+ {
+ m_pNetDnsRequest = Net::dnsRequest(hostname);
+ if(!m_pNetDnsRequest)
+ {
+ return DNS_IF; //Interface did not return a NetDnsRequest (usually because a default interface does not exist)
+ }
+ m_pNetDnsRequest->setOnReply(this, &DNSRequest::onNetDnsReply);
+ return DNS_OK;
+ }
+ else
+ {
+ return DNS_INUSE; //The previous req has not completed
+ }
+}
+
+DNSRequestErr DNSRequest::resolve(Host* pHost)
+{
+ if(!m_pNetDnsRequest)
+ {
+ m_pNetDnsRequest = Net::dnsRequest(pHost);
+ if(!m_pNetDnsRequest)
+ {
+ return DNS_IF; //Interface did not return a NetDnsRequest (usually because a default interface does not exist)
+ }
+ m_pNetDnsRequest->setOnReply(this, &DNSRequest::onNetDnsReply);
+ return DNS_OK;
+ }
+ else
+ {
+ return DNS_INUSE; //The previous req has not completed
+ }
+}
+
+//Callbacks
+void DNSRequest::setOnReply( void (*pMethod)(DNSReply) )
+{
+ m_pCb = pMethod;
+}
+
+#if 0 //For doc only
+template<class T>
+void DNSRequest::setOnReply( T* pItem, void (T::*pMethod)(DNSReply) )
+{
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(DNSReply)) pMethod;
+}
+#endif
+
+DNSRequestErr DNSRequest::getResult(IpAddr* pIp)
+{
+ if(!m_pNetDnsRequest)
+ {
+ return DNS_SETUP;
+ }
+ m_pNetDnsRequest->getResult(pIp);
+ return DNS_OK;
+}
+
+DNSRequestErr DNSRequest::close()
+{
+ if(!m_pNetDnsRequest)
+ {
+ return DNS_SETUP;
+ }
+ m_pNetDnsRequest->close();
+ m_pNetDnsRequest = NULL;
+ return DNS_OK;
+}
+
+void DNSRequest::onNetDnsReply(NetDnsReply r)
+{
+ if(m_pCbItem && m_pCbMeth)
+ (m_pCbItem->*m_pCbMeth)((DNSReply) r);
+ else if(m_pCb)
+ m_pCb((DNSReply) r);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/api/DNSRequest.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,131 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+/** \file
+DNS Request header file
+*/
+
+#ifndef DNSREQUEST_H
+#define DNSREQUEST_H
+
+#include "core/net.h"
+#include "core/ipaddr.h"
+#include "core/host.h"
+//Essentially it is a safe interface to NetDnsRequest
+
+///DNS Request error codes
+enum DNSRequestErr
+{
+ __DNS_MIN = -0xFFFF,
+ DNS_SETUP, ///<DNSRequest not properly configured
+ DNS_IF, ///<Interface has problems, does not exist or is not initialized
+ DNS_MEM, ///<Not enough mem
+ DNS_INUSE, ///<Interface / Port is in use
+ DNS_PROCESSING, ///<Request has not completed
+//...
+ DNS_OK = 0 ///<Success
+};
+
+///DNS Request Result Events
+enum DNSReply
+{
+ DNS_PRTCL,
+ DNS_NOTFOUND, ///Hostname is unknown
+ DNS_ERROR, ///Problem with DNS Service
+ //...
+ DNS_FOUND,
+};
+
+class NetDnsRequest;
+enum NetDnsReply;
+
+///This is a simple DNS Request class
+/**
+ This class exposes an API to deal with DNS Requests
+*/
+class DNSRequest
+{
+public:
+ ///Creates a new request
+ DNSRequest();
+
+ ///Terminates and closes request
+ ~DNSRequest();
+
+ ///Resolves an hostname
+ /**
+ @param hostname : hostname to resolve
+ */
+ DNSRequestErr resolve(const char* hostname);
+
+ ///Resolves an hostname
+ /**
+ @param host : hostname to resolve, the result will be stored in the IpAddr field of this object
+ */
+ DNSRequestErr resolve(Host* pHost);
+
+ ///Setups callback
+ /**
+ The callback function will be called on result.
+ @param pMethod : callback function
+ */
+ void setOnReply( void (*pMethod)(DNSReply) );
+
+ class CDummy;
+ ///Setups callback
+ /**
+ The callback function will be called on result.
+ @param pItem : instance of class on which to execute the callback method
+ @param pMethod : callback method
+ */
+ template<class T>
+ void setOnReply( T* pItem, void (T::*pMethod)(DNSReply) )
+ {
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(DNSReply)) pMethod;
+ }
+
+ ///Gets IP address once it has been resolved
+ /**
+ @param pIp : pointer to an IpAddr instance in which to store the resolved IP address
+ */
+ DNSRequestErr getResult(IpAddr* pIp);
+
+ ///Closes DNS Request before completion
+ DNSRequestErr close();
+
+protected:
+ void onNetDnsReply(NetDnsReply r);
+ DNSRequestErr checkInst();
+
+private:
+ NetDnsRequest* m_pNetDnsRequest;
+
+ CDummy* m_pCbItem;
+ void (CDummy::*m_pCbMeth)(DNSReply);
+
+ void (*m_pCb)(DNSReply);
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/api/TCPSocket.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,147 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "TCPSocket.h"
+#include "if/net/nettcpsocket.h"
+
+TCPSocket::TCPSocket() : m_pNetTcpSocket(NULL), m_pCbItem(NULL), m_pCbMeth(NULL), m_pCb(NULL)
+{
+
+}
+
+TCPSocket::TCPSocket(NetTcpSocket* pNetTcpSocket) : m_pNetTcpSocket(pNetTcpSocket), m_pCbItem(NULL), m_pCbMeth(NULL), m_pCb(NULL)
+{
+ m_pNetTcpSocket->setOnEvent(this, &TCPSocket::onNetTcpSocketEvent);
+}
+
+TCPSocket::~TCPSocket() //close()
+{
+ close();
+}
+
+TCPSocketErr TCPSocket::bind(const Host& me)
+{
+ TCPSocketErr tcpSocketErr = checkInst();
+ if(tcpSocketErr)
+ return tcpSocketErr;
+ return (TCPSocketErr) m_pNetTcpSocket->bind(me);
+}
+
+TCPSocketErr TCPSocket::listen()
+{
+ TCPSocketErr tcpSocketErr = checkInst();
+ if(tcpSocketErr)
+ return tcpSocketErr;
+ return (TCPSocketErr) m_pNetTcpSocket->listen();
+}
+
+TCPSocketErr TCPSocket::connect(const Host& host)
+{
+ TCPSocketErr tcpSocketErr = checkInst();
+ if(tcpSocketErr)
+ return tcpSocketErr;
+ return (TCPSocketErr) m_pNetTcpSocket->connect(host);
+}
+
+TCPSocketErr TCPSocket::accept(Host* pClient, TCPSocket** ppNewTCPSocket)
+{
+ TCPSocketErr tcpSocketErr = checkInst();
+ if(tcpSocketErr)
+ return tcpSocketErr;
+ NetTcpSocket* pNewNetTcpSocket;
+ tcpSocketErr = (TCPSocketErr) m_pNetTcpSocket->accept(pClient, &pNewNetTcpSocket);
+ if(pNewNetTcpSocket)
+ *ppNewTCPSocket = new TCPSocket(pNewNetTcpSocket);
+ return tcpSocketErr;
+}
+
+int /*if < 0 : TCPSocketErr*/ TCPSocket::send(const char* buf, int len)
+{
+ TCPSocketErr tcpSocketErr = checkInst();
+ if(tcpSocketErr)
+ return tcpSocketErr;
+ return m_pNetTcpSocket->send(buf, len);
+}
+
+int /*if < 0 : TCPSocketErr*/ TCPSocket::recv(char* buf, int len)
+{
+ TCPSocketErr tcpSocketErr = checkInst();
+ if(tcpSocketErr)
+ return tcpSocketErr;
+ return m_pNetTcpSocket->recv(buf, len);
+}
+
+TCPSocketErr TCPSocket::close()
+{
+ if(!m_pNetTcpSocket)
+ return TCPSOCKET_SETUP;
+ m_pNetTcpSocket->resetOnEvent();
+ TCPSocketErr tcpSocketErr = (TCPSocketErr) m_pNetTcpSocket->close(); //Close (can already be closed)
+ Net::releaseTcpSocket(m_pNetTcpSocket); //And release it so it can be freed when properly removed
+ m_pNetTcpSocket = NULL;
+ return tcpSocketErr;
+}
+
+//Callbacks
+void TCPSocket::setOnEvent( void (*pMethod)(TCPSocketEvent) )
+{
+ m_pCb = pMethod;
+}
+
+#if 0 //For info only
+template<class T>
+void TCPSocket::setOnEvent( T* pItem, void (T::*pMethod)(TCPSocketEvent) )
+{
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(TCPSocketEvent)) pMethod;
+}
+#endif
+
+void TCPSocket::resetOnEvent() //Disable callback
+{
+ m_pCb = NULL;
+ m_pCbItem = NULL;
+ m_pCbMeth = NULL;
+}
+
+void TCPSocket::onNetTcpSocketEvent(NetTcpSocketEvent e)
+{
+ if(m_pCbItem && m_pCbMeth)
+ (m_pCbItem->*m_pCbMeth)((TCPSocketEvent) e);
+ else if(m_pCb)
+ m_pCb((TCPSocketEvent) e);
+}
+
+TCPSocketErr TCPSocket::checkInst()
+{
+ if(!m_pNetTcpSocket)
+ {
+ m_pNetTcpSocket = Net::tcpSocket();
+ if(!m_pNetTcpSocket)
+ {
+ return TCPSOCKET_IF; //Interface did not return a socket (usually because a default interface does not exist)
+ }
+ m_pNetTcpSocket->setOnEvent(this, &TCPSocket::onNetTcpSocketEvent);
+ }
+ return TCPSOCKET_OK;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/api/TCPSocket.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,148 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+/** \file
+TCP Socket header file
+*/
+
+#ifndef TCPSOCKET_H
+#define TCPSOCKET_H
+
+#include "core/net.h"
+#include "core/host.h"
+//Essentially it is a safe interface to NetTcpSocket
+
+///TCP Socket error codes
+enum TCPSocketErr
+{
+ __TCPSOCKET_MIN = -0xFFFF,
+ TCPSOCKET_SETUP, ///<TCPSocket not properly configured
+ TCPSOCKET_TIMEOUT, ///<Connection timed out
+ TCPSOCKET_IF, ///<Interface has problems, does not exist or is not initialized
+ TCPSOCKET_MEM, ///<Not enough mem
+ TCPSOCKET_INUSE, ///<Interface / Port is in use
+ TCPSOCKET_EMPTY, ///<Connections queue is empty
+ TCPSOCKET_RST, ///<Connection was reset by remote host
+//...
+ TCPSOCKET_OK = 0 ///<Success
+};
+
+///TCP Socket Events
+enum TCPSocketEvent
+{
+ TCPSOCKET_CONNECTED, ///<Connected to host
+ TCPSOCKET_ACCEPT, ///<Client is connected, must call accept() to get a new Socket
+ TCPSOCKET_READABLE, ///<Data in buf
+ TCPSOCKET_WRITEABLE, ///<Can write data to buf
+ TCPSOCKET_CONTIMEOUT, ///<Connection timed out
+ TCPSOCKET_CONRST, ///<Connection was reset by remote host
+ TCPSOCKET_CONABRT, ///<Connection was aborted
+ TCPSOCKET_ERROR, ///<Unknown error
+ TCPSOCKET_DISCONNECTED ///<Disconnected
+};
+
+class NetTcpSocket;
+enum NetTcpSocketEvent;
+
+///This is a simple TCP Socket class
+/**
+ This class exposes an API to deal with TCP Sockets
+*/
+class TCPSocket
+{
+public:
+ ///Creates a new socket
+ TCPSocket();
+protected:
+ TCPSocket(NetTcpSocket* pNetTcpSocket);
+public:
+ ///Closes if needed and destroys the socket
+ ~TCPSocket(); //close()
+
+ ///Binds the socket to (local) host
+ TCPSocketErr bind(const Host& me);
+
+ ///Starts listening
+ TCPSocketErr listen();
+
+ ///Connects socket to host
+ TCPSocketErr connect(const Host& host);
+
+ ///Accepts connection from client and gets connected socket
+ TCPSocketErr accept(Host* pClient, TCPSocket** ppNewTcpSocket);
+
+ ///Sends data
+ /*
+ @return a negative error code or the number of bytes transmitted
+ */
+ int /*if < 0 : TCPSocketErr*/ send(const char* buf, int len);
+
+ ///Receives data
+ /*
+ @return a negative error code or the number of bytes received
+ */
+ int /*if < 0 : TCPSocketErr*/ recv(char* buf, int len);
+
+ /* TODO NTH : printf / scanf helpers that call send/recv */
+
+ ///Closes socket
+ TCPSocketErr close();
+
+ //Callbacks
+ ///Setups callback
+ /**
+ @param pMethod : callback function
+ */
+ void setOnEvent( void (*pMethod)(TCPSocketEvent) );
+
+ class CDummy;
+ ///Setups callback
+ /**
+ @param pItem : instance of class on which to execute the callback method
+ @param pMethod : callback method
+ */
+ template<class T>
+ void setOnEvent( T* pItem, void (T::*pMethod)(TCPSocketEvent) )
+ {
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(TCPSocketEvent)) pMethod;
+ }
+
+ ///Disables callback
+ void resetOnEvent();
+
+protected:
+ void onNetTcpSocketEvent(NetTcpSocketEvent e);
+ TCPSocketErr checkInst();
+
+private:
+ NetTcpSocket* m_pNetTcpSocket;
+
+ CDummy* m_pCbItem;
+ void (CDummy::*m_pCbMeth)(TCPSocketEvent);
+
+ void (*m_pCb)(TCPSocketEvent);
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/api/UDPSocket.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,114 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "UDPSocket.h"
+#include "if/net/netudpsocket.h"
+
+UDPSocket::UDPSocket() : m_pNetUdpSocket(NULL), m_pCbItem(NULL), m_pCbMeth(NULL), m_pCb(NULL)
+{
+
+}
+
+UDPSocket::~UDPSocket() //close()
+{
+ close();
+}
+
+UDPSocketErr UDPSocket::bind(const Host& me)
+{
+ UDPSocketErr udpSocketErr = checkInst();
+ if(udpSocketErr)
+ return udpSocketErr;
+ return (UDPSocketErr) m_pNetUdpSocket->bind(me);
+}
+
+int /*if < 0 : UDPSocketErr*/ UDPSocket::sendto(const char* buf, int len, Host* pHost)
+{
+ UDPSocketErr udpSocketErr = checkInst();
+ if(udpSocketErr)
+ return udpSocketErr;
+ return m_pNetUdpSocket->sendto(buf, len, pHost);
+}
+
+int /*if < 0 : UDPSocketErr*/ UDPSocket::recvfrom(char* buf, int len, Host* pHost)
+{
+ UDPSocketErr udpSocketErr = checkInst();
+ if(udpSocketErr)
+ return udpSocketErr;
+ return m_pNetUdpSocket->recvfrom(buf, len, pHost);
+}
+
+UDPSocketErr UDPSocket::close()
+{
+ if(!m_pNetUdpSocket)
+ return UDPSOCKET_SETUP;
+ m_pNetUdpSocket->resetOnEvent();
+ UDPSocketErr udpSocketErr = (UDPSocketErr) m_pNetUdpSocket->close(); //Close (can already be closed)
+ Net::releaseUdpSocket(m_pNetUdpSocket); //And release it so it can be freed when properly removed
+ m_pNetUdpSocket = NULL;
+ return udpSocketErr;
+}
+
+//Callbacks
+void UDPSocket::setOnEvent( void (*pMethod)(UDPSocketEvent) )
+{
+ m_pCb = pMethod;
+}
+
+#if 0 //For info only
+template<class T>
+void UDPSocket::setOnEvent( T* pItem, void (T::*pMethod)(UDPSocketEvent) )
+{
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(UDPSocketEvent)) pMethod;
+}
+#endif
+
+void UDPSocket::resetOnEvent() //Disable callback
+{
+ m_pCb = NULL;
+ m_pCbItem = NULL;
+ m_pCbMeth = NULL;
+}
+
+void UDPSocket::onNetUdpSocketEvent(NetUdpSocketEvent e)
+{
+ if(m_pCbItem && m_pCbMeth)
+ (m_pCbItem->*m_pCbMeth)((UDPSocketEvent) e);
+ else if(m_pCb)
+ m_pCb((UDPSocketEvent) e);
+}
+
+UDPSocketErr UDPSocket::checkInst()
+{
+ if(!m_pNetUdpSocket)
+ {
+ m_pNetUdpSocket = Net::udpSocket();
+ if(!m_pNetUdpSocket)
+ {
+ return UDPSOCKET_IF; //Interface did not return a socket (usually because a default interface does not exist)
+ }
+ m_pNetUdpSocket->setOnEvent(this, &UDPSocket::onNetUdpSocketEvent);
+ }
+ return UDPSOCKET_OK;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/api/UDPSocket.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,128 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+/** \file
+UDP Socket header file
+*/
+
+#ifndef UDPSOCKET_H
+#define UDPSOCKET_H
+
+#include "core/net.h"
+#include "core/host.h"
+//Essentially it is a safe interface to NetUdpSocket
+
+///UDP Socket error codes
+enum UDPSocketErr
+{
+ __UDPSOCKET_MIN = -0xFFFF,
+ UDPSOCKET_SETUP, ///<UDPSocket not properly configured
+ UDPSOCKET_IF, ///<Interface has problems, does not exist or is not initialized
+ UDPSOCKET_MEM, ///<Not enough mem
+ UDPSOCKET_INUSE, ///<Interface / Port is in use
+//...
+ UDPSOCKET_OK = 0 ///<Success
+};
+
+///UDP Socket Event(s)
+enum UDPSocketEvent //Only one event here for now, but keeps that model in case we need to implement some others
+{
+ UDPSOCKET_READABLE, ///<Data in buf
+};
+
+class NetUdpSocket;
+enum NetUdpSocketEvent;
+
+///This is a simple UDP Socket class
+/**
+ This class exposes an API to deal with UDP Sockets
+*/
+class UDPSocket
+{
+public:
+ ///Creates a new socket
+ UDPSocket();
+
+ ///Closes and destroys socket
+ ~UDPSocket(); //close()
+
+ ///Binds the socket to local host or a multicast address
+ UDPSocketErr bind(const Host& me);
+
+ ///Sends data
+ /*
+ @param pHost : host to send data to
+ @return a negative error code or the number of bytes transmitted
+ */
+ int /*if < 0 : UDPSocketErr*/ sendto(const char* buf, int len, Host* pHost);
+
+ ///Receives data
+ /*
+ @param pHost : host from which this piece of data comes from
+ @return a negative error code or the number of bytes received
+ */
+ int /*if < 0 : UDPSocketErr*/ recvfrom(char* buf, int len, Host* pHost);
+
+ /* TODO NTH : printf / scanf helpers that call send/recv */
+
+ ///Closes socket
+ UDPSocketErr close();
+
+ //Callbacks
+ ///Setups callback
+ /**
+ @param pMethod : callback function
+ */
+ void setOnEvent( void (*pMethod)(UDPSocketEvent) );
+
+ class CDummy;
+ ///Setups callback
+ /**
+ @param pItem : instance of class on which to execute the callback method
+ @param pMethod : callback method
+ */
+ template<class T>
+ void setOnEvent( T* pItem, void (T::*pMethod)(UDPSocketEvent) )
+ {
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(UDPSocketEvent)) pMethod;
+ }
+
+ ///Disables callback
+ void resetOnEvent();
+
+protected:
+ void onNetUdpSocketEvent(NetUdpSocketEvent e);
+ UDPSocketErr checkInst();
+
+private:
+ NetUdpSocket* m_pNetUdpSocket;
+
+ CDummy* m_pCbItem;
+ void (CDummy::*m_pCbMeth)(UDPSocketEvent);
+
+ void (*m_pCb)(UDPSocketEvent);
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/host.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,109 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef HOST_H
+#define HOST_H
+
+#include "ipaddr.h"
+#include <string.h>
+
+///Host information container
+/**
+This class is a container for data relative to a connection:
+- IP Address
+- Port number
+- Host Name
+*/
+class Host
+{
+public:
+ ///Initiliazes host with null values
+ Host() : m_ip(0,0,0,0), m_port(0), m_name(NULL)
+ {
+
+ }
+
+ ///Initializes host
+ Host(const IpAddr& ip, const int& port, const char* name="" ) : m_ip(ip), m_port(port), m_name(NULL)
+ {
+ setName(name);
+ }
+
+ ~Host()
+ {
+ if(m_name)
+ {
+ delete[] m_name;
+ }
+ }
+
+ ///Returns IP address
+ const IpAddr& getIp() const
+ {
+ return m_ip;
+ }
+
+ ///Returns port number
+ const int& getPort() const
+ {
+ return m_port;
+ }
+
+ ///Returns host name
+ const char* getName() const
+ {
+ return m_name;
+ }
+
+ ///Sets IP address
+ void setIp(const IpAddr& ip)
+ {
+ m_ip = ip;
+ }
+
+ ///Sets port number
+ void setPort(int port)
+ {
+ m_port = port;
+ }
+
+ ///Sets host name
+ void setName(const char* name)
+ {
+ if(m_name)
+ delete[] m_name;
+ int len = strlen(name);
+ if(len)
+ {
+ m_name = new char[len+1];
+ strcpy(m_name, name);
+ }
+ }
+
+private:
+ IpAddr m_ip;
+ int m_port;
+ char* m_name;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/ipaddr.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,104 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "ipaddr.h"
+
+#include "netCfg.h"
+#if NET_LWIP_STACK
+#include "lwip/ip_addr.h"
+#endif
+
+
+#if NET_LWIP_STACK
+IpAddr::IpAddr(ip_addr_t* pIp)
+{
+ *((uint32_t*)m_ip) = pIp->addr;
+}
+#endif
+
+///Initializes IP address with provided values
+IpAddr::IpAddr(uint8_t ip0, uint8_t ip1, uint8_t ip2, uint8_t ip3)
+{
+ //We are in LE
+ m_ip[0] = ip0;
+ m_ip[1] = ip1;
+ m_ip[2] = ip2;
+ m_ip[3] = ip3;
+}
+
+///Initializes IP address with null values
+IpAddr::IpAddr()
+{
+ m_ip[0] = 0;
+ m_ip[1] = 0;
+ m_ip[2] = 0;
+ m_ip[3] = 0;
+}
+
+
+#if NET_LWIP_STACK
+ip_addr_t IpAddr::getStruct() const
+{
+ ip_addr_t ip_struct;
+ ip_struct.addr = *((uint32_t*)m_ip);
+ return ip_struct;
+}
+#endif
+
+uint8_t IpAddr::operator[](unsigned int i) const
+{
+ uint8_t null = 0;
+ if( i > 3 )
+ return null;
+ return m_ip[i];
+}
+
+bool IpAddr::isEq(const IpAddr& b) const
+{
+ return (*((uint32_t*)m_ip) == *((uint32_t*)(b.m_ip)));
+}
+
+bool IpAddr::operator==(const IpAddr& b) const
+{
+ return isEq(b);
+}
+
+bool IpAddr::operator!=(const IpAddr& b) const
+{
+ return !(operator==(b));
+}
+
+bool IpAddr::isNull() const
+{
+ return (*((uint32_t*)m_ip) == 0);
+}
+
+bool IpAddr::isBroadcast() const
+{
+ return (*((uint32_t*)m_ip) == 0xFFFFFFFF);
+}
+
+bool IpAddr::isMulticast() const
+{
+ return ((m_ip[0] & 0xF0) == 0xE0);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/ipaddr.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,98 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef IPADDR_H
+#define IPADDR_H
+
+#include "netCfg.h"
+#if NET_LWIP_STACK
+typedef struct ip_addr ip_addr_t;
+#endif
+
+#include "stdint.h"
+
+///IP Address container
+/**
+This class is a container for an IPv4 address.
+*/
+class IpAddr //Basically a C++ frontend to ip_addr_t
+{
+public:
+ #if NET_LWIP_STACK
+ IpAddr(ip_addr_t* pIp);
+ #endif
+
+ ///Initializes IP address with provided values
+ IpAddr(uint8_t ip0, uint8_t ip1, uint8_t ip2, uint8_t ip3);
+
+ ///Initializes IP address with null values
+ IpAddr();
+
+ #if NET_LWIP_STACK
+ ip_addr_t getStruct() const;
+ #endif
+
+ ///Returns IP address byte #
+ uint8_t operator[](unsigned int i) const;
+
+ ///Compares too addresses
+ /**
+ @return true if the two addresses are equal
+ */
+ bool isEq(const IpAddr& b) const;
+
+ ///Compares too addresses
+ /**
+ @return true if the two addresses are equal
+ */
+ bool operator==(const IpAddr& b) const;
+
+ ///Compares too addresses
+ /**
+ @return true if the two addresses are different
+ */
+ bool operator!=(const IpAddr& b) const;
+
+ ///Checks whether the address is null
+ /**
+ @return true if the address is null
+ */
+ bool isNull() const;
+
+ ///Checks whether the address is a broadcast address
+ /**
+ @return true if the address is a broadcast address
+ */
+ bool isBroadcast() const;
+
+ ///Checks whether the address is a multicast address
+ /**
+ @return true if the address is a multicast address
+ */
+ bool isMulticast() const;
+
+private:
+ uint8_t m_ip[4];
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/net.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,229 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "net.h"
+
+#include "host.h"
+#include "ipaddr.h"
+#include "netservice.h"
+#include "if/net/netif.h"
+#include "if/net/nettcpsocket.h"
+#include "if/net/netudpsocket.h"
+#include "if/net/netdnsrequest.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+Net::Net() : m_defaultIf(NULL), m_lpIf(), m_lpNetTcpSocket(), m_lpNetUdpSocket()
+{
+
+}
+
+Net::~Net()
+{
+
+}
+
+
+void Net::poll()
+{
+ //DBG("\r\nNet : Services polling\r\n");
+
+ //Poll Services
+ NetService::servicesPoll();
+
+ //DBG("\r\nNet : Interfaces polling\r\n");
+
+ //Poll Interfaces
+ list<NetIf*>::iterator pIfIt;
+
+ for ( pIfIt = net().m_lpIf.begin() ; pIfIt != net().m_lpIf.end(); pIfIt++ )
+ {
+ (*pIfIt)->poll();
+ }
+
+ //DBG("\r\nNet : Sockets polling\r\n");
+
+ //Poll Tcp Sockets
+ list<NetTcpSocket*>::iterator pNetTcpSocketIt;
+
+ for ( pNetTcpSocketIt = net().m_lpNetTcpSocket.begin() ; pNetTcpSocketIt != net().m_lpNetTcpSocket.end(); )
+ {
+ (*pNetTcpSocketIt)->poll();
+
+ if( (*pNetTcpSocketIt)->m_closed && !((*pNetTcpSocketIt)->m_refs) )
+ {
+ (*pNetTcpSocketIt)->m_removed = true;
+ delete (*pNetTcpSocketIt);
+ (*pNetTcpSocketIt) = NULL;
+ pNetTcpSocketIt = net().m_lpNetTcpSocket.erase(pNetTcpSocketIt);
+ }
+ else
+ {
+ pNetTcpSocketIt++;
+ }
+ }
+
+ //Poll Udp Sockets
+ list<NetUdpSocket*>::iterator pNetUdpSocketIt;
+
+ for ( pNetUdpSocketIt = net().m_lpNetUdpSocket.begin() ; pNetUdpSocketIt != net().m_lpNetUdpSocket.end(); )
+ {
+ (*pNetUdpSocketIt)->poll();
+
+ if( (*pNetUdpSocketIt)->m_closed && !((*pNetUdpSocketIt)->m_refs) )
+ {
+ (*pNetUdpSocketIt)->m_removed = true;
+ delete (*pNetUdpSocketIt);
+ (*pNetUdpSocketIt) = NULL;
+ pNetUdpSocketIt = net().m_lpNetUdpSocket.erase(pNetUdpSocketIt);
+ }
+ else
+ {
+ pNetUdpSocketIt++;
+ }
+ }
+
+
+}
+
+NetTcpSocket* Net::tcpSocket(NetIf& netif) {
+ NetTcpSocket* pNetTcpSocket = netif.tcpSocket();
+ pNetTcpSocket->m_refs++;
+ return pNetTcpSocket;
+}
+
+NetTcpSocket* Net::tcpSocket() { //NetTcpSocket on default if
+ if ( net().m_defaultIf == NULL )
+ return NULL;
+ NetTcpSocket* pNetTcpSocket = net().m_defaultIf->tcpSocket();
+ pNetTcpSocket->m_refs++;
+ return pNetTcpSocket;
+}
+
+void Net::releaseTcpSocket(NetTcpSocket* pNetTcpSocket)
+{
+ pNetTcpSocket->m_refs--;
+ if(!pNetTcpSocket->m_closed && !pNetTcpSocket->m_refs)
+ pNetTcpSocket->close();
+}
+
+NetUdpSocket* Net::udpSocket(NetIf& netif) {
+ NetUdpSocket* pNetUdpSocket = netif.udpSocket();
+ pNetUdpSocket->m_refs++;
+ return pNetUdpSocket;
+}
+
+NetUdpSocket* Net::udpSocket() { //NetTcpSocket on default if
+ if ( net().m_defaultIf == NULL )
+ return NULL;
+ NetUdpSocket* pNetUdpSocket = net().m_defaultIf->udpSocket();
+ pNetUdpSocket->m_refs++;
+ return pNetUdpSocket;
+}
+
+void Net::releaseUdpSocket(NetUdpSocket* pNetUdpSocket)
+{
+ pNetUdpSocket->m_refs--;
+ if(!pNetUdpSocket->m_closed && !pNetUdpSocket->m_refs)
+ pNetUdpSocket->close();
+}
+
+NetDnsRequest* Net::dnsRequest(const char* hostname, NetIf& netif) {
+ return netif.dnsRequest(hostname);
+}
+
+NetDnsRequest* Net::dnsRequest(const char* hostname) { //Create a new NetDnsRequest object from default if
+ if ( net().m_defaultIf == NULL )
+ return NULL;
+ return net().m_defaultIf->dnsRequest(hostname);
+}
+
+NetDnsRequest* Net::dnsRequest(Host* pHost, NetIf& netif)
+{
+ return netif.dnsRequest(pHost);
+}
+
+NetDnsRequest* Net::dnsRequest(Host* pHost) //Creats a new NetDnsRequest object from default if
+{
+ if ( net().m_defaultIf == NULL )
+ return NULL;
+ return net().m_defaultIf->dnsRequest(pHost);
+}
+
+void Net::setDefaultIf(NetIf& netif) {
+ net().m_defaultIf = &netif;
+}
+
+void Net::setDefaultIf(NetIf* pIf)
+{
+ net().m_defaultIf = pIf;
+}
+
+NetIf* Net::getDefaultIf() {
+ return net().m_defaultIf;
+}
+
+void Net::registerIf(NetIf* pIf)
+{
+ net().m_lpIf.push_back(pIf);
+}
+
+void Net::unregisterIf(NetIf* pIf)
+{
+ if( net().m_defaultIf == pIf )
+ net().m_defaultIf = NULL;
+ net().m_lpIf.remove(pIf);
+}
+
+void Net::registerNetTcpSocket(NetTcpSocket* pNetTcpSocket)
+{
+ net().m_lpNetTcpSocket.push_back(pNetTcpSocket);
+ DBG("\r\nNetTcpSocket [ + %p ] %d\r\n", (void*)pNetTcpSocket, net().m_lpNetTcpSocket.size());
+}
+
+void Net::unregisterNetTcpSocket(NetTcpSocket* pNetTcpSocket)
+{
+ DBG("\r\nNetTcpSocket [ - %p ] %d\r\n", (void*)pNetTcpSocket, net().m_lpNetTcpSocket.size() - 1);
+ if(!pNetTcpSocket->m_removed)
+ net().m_lpNetTcpSocket.remove(pNetTcpSocket);
+}
+
+void Net::registerNetUdpSocket(NetUdpSocket* pNetUdpSocket)
+{
+ net().m_lpNetUdpSocket.push_back(pNetUdpSocket);
+ DBG("\r\nNetUdpSocket [ + %p ] %d\r\n", (void*)pNetUdpSocket, net().m_lpNetUdpSocket.size());
+}
+
+void Net::unregisterNetUdpSocket(NetUdpSocket* pNetUdpSocket)
+{
+ DBG("\r\nNetUdpSocket [ - %p ] %d\r\n", (void*)pNetUdpSocket, net().m_lpNetUdpSocket.size() - 1);
+ if(!pNetUdpSocket->m_removed)
+ net().m_lpNetUdpSocket.remove(pNetUdpSocket);
+}
+
+Net& Net::net()
+{
+ static Net* pInst = new Net(); //Called only once
+ return *pInst;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/net.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,101 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef NET_H
+#define NET_H
+
+class NetIf;
+class NetTcpSocket;
+class NetUdpSocket;
+class NetDnsRequest;
+
+#include <list>
+using std::list;
+
+/*
+#include "host.h"
+#include "ipaddr.h"
+#include "netservice.h"
+#include "if/net/netif.h"
+#include "if/net/nettcpsocket.h"
+#include "if/net/netudpsocket.h"
+#include "if/net/netdnsrequest.h"
+*/
+
+class Host;
+class NetIf;
+class NetTcpSocket;
+class NetUdpSocket;
+class NetDnsRequest;
+
+class Net
+{
+private:
+ Net();
+ ~Net();
+public:
+ static void poll(); //Poll every if & socket
+
+ static NetTcpSocket* tcpSocket(NetIf& netif);
+ static NetTcpSocket* tcpSocket(); //Socket on default if
+ static void releaseTcpSocket(NetTcpSocket* pNetTcpSocket);
+
+ static NetUdpSocket* udpSocket(NetIf& netif);
+ static NetUdpSocket* udpSocket(); //Socket on default if
+ static void releaseUdpSocket(NetUdpSocket* pNetUdpSocket);
+
+ static NetDnsRequest* dnsRequest(const char* hostname, NetIf& netif);
+ static NetDnsRequest* dnsRequest(const char* hostname); //Create a new NetDnsRequest object from default if
+
+ static NetDnsRequest* dnsRequest(Host* pHost, NetIf& netif);
+ static NetDnsRequest* dnsRequest(Host* pHost); //Create a new NetDnsRequest object from default if
+
+ static void setDefaultIf(NetIf& netif); //Deprecated
+ static void setDefaultIf(NetIf* pIf);
+ static NetIf* getDefaultIf();
+
+protected:
+ friend class NetIf;
+ friend class NetTcpSocket;
+ friend class NetUdpSocket;
+
+ static void registerIf(NetIf* pIf);
+ static void unregisterIf(NetIf* pIf);
+
+ static void registerNetTcpSocket(NetTcpSocket* pNetTcpSocket);
+ static void unregisterNetTcpSocket(NetTcpSocket* pNetTcpSocket);
+
+ static void registerNetUdpSocket(NetUdpSocket* pNetUdpSocket);
+ static void unregisterNetUdpSocket(NetUdpSocket* pNetUdpSocket);
+
+private:
+ static Net& net(); //Return inst of singleton
+
+ NetIf* m_defaultIf;
+
+ list<NetIf*> m_lpIf;
+ list<NetTcpSocket*> m_lpNetTcpSocket;
+ list<NetUdpSocket*> m_lpNetUdpSocket;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/netservice.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,88 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "netservice.h"
+#include "net.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+NetService::NetService(bool owned /*= true*/) : m_closed(false), m_removed(false), m_owned(owned)
+{
+ NetService::lpServices().push_back(this);
+ DBG("\r\n[ + %p ] %d\r\n", (void*)this, lpServices().size());
+}
+
+NetService::~NetService()
+{
+ // DBG("\r\nService removed\r\n");
+// DBG("\r\nNow %d services running\r\n", lpServices().size()-1);
+ DBG("\r\n[ - %p ] %d\r\n", (void*)this, lpServices().size()-1);
+ if((!m_owned) || (!m_removed)) //Destructor was not called by servicesPoll()
+ {
+ if(m_owned)
+ DBG("\r\nWARN!!!Service removed in dtor!!!\r\n");
+ NetService::lpServices().remove(this);
+ }
+}
+
+void NetService::poll()
+{
+
+}
+
+void NetService::servicesPoll() //Poll all registered services & destroy closed ones
+{
+ list<NetService*>::iterator it;
+ DBG("\r\nServices polling over %d services\r\n", lpServices().size());
+ for( it = lpServices().begin(); it != lpServices().end(); )
+ {
+ if( (*it)->m_owned && (*it)->m_closed )
+ {
+ DBG("\r\nService %p is flagged as closed\r\n", (*it));
+ (*it)->m_removed = true;
+ delete (*it);
+ it = lpServices().erase(it);
+ }
+ else
+ {
+ //DBG("Service %p polling start\n", (*it));
+ (*it)->poll();
+ //DBG("Service %p polling end\n", (*it));
+ it++;
+ }
+ }
+
+}
+
+void NetService::close()
+{
+ DBG("\r\nService %p to be closed (owned = %d)\r\n", this, m_owned);
+ m_closed = true;
+}
+
+list<NetService*>& NetService::lpServices()
+{
+ static list<NetService*>* pInst = new list<NetService*>(); //Called only once
+ return *pInst;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/netservice.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,67 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+/**
+\file Net Service base class header file
+*/
+
+#ifndef NETSERVICE_H
+#define NETSERVICE_H
+
+#include <list>
+using std::list;
+
+///Net Service base class
+/**
+Each connection-oriented object can register as service (by inheriting this class), so that it is polled regularly.
+It notifies the pool when the connection is terminated so that it can be destroyed.
+*/
+class NetService
+{
+public:
+ ///Instantiates a new service
+ /**
+ @param owned If true the object is owned by the pool and will be destroyed on closure.
+ */
+ NetService(bool owned = true); //Is owned by the pool?
+ virtual ~NetService();
+
+ ///This method can be inherited so that it is called on each @a Net::poll() call.
+ virtual void poll();
+
+ static void servicesPoll(); //Poll all registered services & destroy closed ones
+
+protected:
+ ///This flags the service as to be destructed if owned by the pool.
+ void close();
+
+private:
+ bool m_closed;
+ bool m_removed;
+ bool m_owned;
+
+ static list<NetService*>& lpServices(); //Helper to prevent static initialization fiasco
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dbg/dbg.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,63 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#define __LWIP_DEBUG
+#define __DEBUG
+#include "dbg.h"
+#include "mbed.h"
+#include <cstdarg>
+
+void DebugStream::debug(const char* format, ...)
+{
+ va_list argp;
+
+ va_start(argp, format);
+ vprintf(format, argp);
+ va_end(argp);
+}
+
+void DebugStream::release()
+{
+
+}
+
+void DebugStream::breakPoint(const char* file, int line)
+{
+ printf("\r\nBREAK in %s at line %d\r\n", file, line);
+ fflush(stdout);
+ getchar();
+ fflush(stdin);
+}
+
+/*
+int snprintf(char *str, int size, const char *format, ...)
+{
+ va_list argp;
+
+ va_start(argp, format);
+ vsprintf(str, format, argp);
+ va_end(argp);
+
+ return strlen(str);
+}
+*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dbg/dbg.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,94 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+/** \file
+Debugging helpers header file
+*/
+
+//#ifdef DBG_H
+//#define DBG_H
+
+#ifdef __LWIP_DEBUG
+#define __DEBUG
+#endif
+
+/*!
+ \def __DEBUG
+ To define to enable debugging in one file
+*/
+
+#ifdef __DEBUG
+
+#ifndef __DEBUGSTREAM
+#define __DEBUGSTREAM
+
+
+class DebugStream
+{
+public:
+static void debug(const char* format, ...);
+static void release();
+static void breakPoint(const char* file, int line);
+private:
+
+};
+
+#undef DBG
+#undef DBG_END
+#undef BREAK
+
+///Debug output (if enabled), same syntax as printf, with heading info
+#define DBG(...) do{ DebugStream::debug("[%s:%s@%d] ", __FILE__, __FUNCTION__, __LINE__); DebugStream::debug(__VA_ARGS__); } while(0);
+
+///Debug output (if enabled), same syntax as printf, no heading info
+#define DBGL(...) do{ DebugStream::debug(__VA_ARGS__); } while(0);
+#define DBG_END DebugStream::release
+
+///Break point usin serial debug interface (if debug enbaled)
+#define BREAK() DebugStream::breakPoint(__FILE__, __LINE__)
+#endif
+
+#else
+#undef DBG
+#undef DBG_END
+#undef BREAK
+#define DBG(...)
+#define DBG_END()
+#define BREAK()
+#endif
+
+#ifdef __LWIP_DEBUG
+#ifndef __SNPRINTF
+#define __SNPRINTF
+#include "mbed.h"
+
+//int snprintf(char *str, int size, const char *format, ...);
+#endif
+#endif
+
+#ifdef __LWIP_DEBUG
+#undef __DEBUG
+#endif
+
+//#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/at/ATIf.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,772 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "ATIf.h"
+#include "mbed.h"
+#include <cstdarg>
+
+#define READ_TIMEOUT 100
+#define TMP_BUF_SIZE 128//512
+
+#define SERIAL_BUF_LEN 512 //Huge buf needed for PPP (esp. when transferring big data chunks, using TCP)
+
+#define BAUDRATE 9600//9600//115200// 19200
+
+#include "netCfg.h"
+#if NET_GPRS
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+ATIf::ATIf() : SerialBuf(SERIAL_BUF_LEN), m_isOpen(false)//, m_signalsEnable(false), m_isOpen(false), m_pCurrentSignal(NULL), m_signals()
+{
+ DBG("New AT If@%p\n", this);
+
+ m_readTimeout = READ_TIMEOUT; //default 1s
+ //tmpBuf = NULL;
+ m_lineMode = false;
+ m_tmpBuf = new char[TMP_BUF_SIZE];
+}
+
+ATIf::~ATIf()
+{
+ if(m_tmpBuf)
+ delete[] m_tmpBuf;
+}
+
+int ATIf::printf(const char* format, ... )
+{
+
+/*if(!m_tmpBuf)
+ m_tmpBuf = new char[TMP_BUF_SIZE]; //is it really necessary ??*/
+ *m_tmpBuf=0;
+
+ int len = 0;
+
+ //
+// flushBuffer();
+//wait(1);
+ //
+
+ va_list argp;
+
+ va_start(argp, format);
+ len += vsprintf(m_tmpBuf, format, argp);
+ va_end(argp);
+
+ //DBG("\r\nOutBuf is : %s, mode is %d.", m_tmpBuf, m_lineMode);
+
+ int err = write( m_tmpBuf, m_lineMode );
+ if (err<0)
+ return 0;
+
+ return len;
+
+}
+
+int ATIf::scanf(const char* format, ... )
+{
+/*if(!m_tmpBuf)
+ m_tmpBuf = new char[TMP_BUF_SIZE];*/
+ int err = read( m_tmpBuf, TMP_BUF_SIZE - 1, m_readTimeout, m_lineMode, 1/*Ensure at least one char is read*/ );
+ if (err<0)
+ return -1;//EOF
+
+ DBG("Scanf'ing:\r\n%s\r\n",m_tmpBuf);
+
+ int len = 0;
+
+ if(strchr(format,'%')) //Ugly, determines wether format string is null or not
+ {
+ va_list argp;
+
+ va_start(argp, format);
+ len += vsscanf(m_tmpBuf, format, argp);
+ va_end(argp);
+ }
+ else //No varargs, call strncmp
+ {
+ /* if(strlen(m_tmpBuf) == 0 )
+ return -1;*/
+ if( !strncmp(m_tmpBuf, format, strlen(format)) )
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ return len;
+
+}
+
+void ATIf::setTimeout(int timeout) //used by scanf
+{
+ m_readTimeout = timeout;
+}
+
+void ATIf::setLineMode(bool lineMode) //Switch btw line & raw fns
+{
+ m_lineMode = lineMode;
+}
+
+#if 0
+void ATIf::setSignals(bool signalsEnable)
+{
+ m_signalsEnable=signalsEnable;
+}
+#endif
+
+#if 0
+template<class T>
+void ATIf::attachSignal( const char* sigName, T* pItem, bool (T::*pMethod)(ATIf*, bool, bool*) ) //Attach Signal ("Unsollicited response code" in Telit_AT_Reference_Guide.pdf) to an handler fn
+{
+ ATSigHandler sig(sigName, (ATSigHandler::CDummy*)pItem, (bool (ATSigHandler::CDummy::*)(ATIf*, bool, bool*))pMethod);
+ m_signals.push_back(sig);
+}
+#endif
+
+#if 0
+void ATIf::detachSignal( const char* sigName )
+{
+ list<ATSigHandler>::iterator it;
+
+ for ( it = m_signals.begin(); it != m_signals.end(); it++ )
+ {
+ if( !strcmp((*it).m_name,sigName) )
+ {
+ m_signals.erase(it);
+ break;
+ }
+ }
+}
+#endif
+
+ATErr ATIf::open(Serial* pSerial) //Deactivate echo, etc
+{
+ DBG("Opening...\n");
+ m_isOpen = true; //Must be set so that the serial port-related fns work
+ //Setup options
+// pSerial->baud(BAUDRATE); //FIXME
+ SerialBuf::attach(pSerial);
+
+ setReadMode(false); //Discard chars
+ setTimeout(1000);
+ setLineMode(true); //Line Mode
+
+ DBG("Trmt...\n");
+ // printf("AT+IPR=%d", BAUDRATE); //FIXME
+ printf("ATZ"); //Reset
+ wait(.100);
+ printf("ATE"); //Deactivate echo
+ wait(.500);
+ flushBuffer();
+
+ DBG("ATZ ATE...\n");
+
+ int len = writeLine("ATV1");
+ ATErr err = AT_OK;
+ if(len<0)
+ err=(ATErr)len;
+
+ if(!err)
+ {
+ err = checkOK();
+ if (err) //No ACK from module
+ {
+ DBG("\r\nOpening port, error %d.", err);
+ if(err==AT_TIMEOUT)
+ err = AT_NOANSWER;
+ }
+ }
+
+ if(err)
+ {
+ SerialBuf::detach();
+ m_isOpen = false;
+ return err;
+ }
+
+ DBG("\r\nNo error.");
+ #if 0//FIXME
+ m_signalsEnable = true;
+ #endif
+ //FIXME:
+// m_pSerial->attach<ATIf>(this, &ATIf::onSerialInterrupt);
+
+ return AT_OK;
+}
+
+#if NET_USB_SERIAL
+ATErr ATIf::open(UsbSerial* pUsbSerial) //Deactivate echo, etc
+{
+ DBG("Opening...\n");
+ m_isOpen = true; //Must be set so that the serial port-related fns work
+ //Setup options
+ SerialBuf::attach(pUsbSerial);
+
+ setReadMode(false); //Discard chars
+ setTimeout(1000);
+ setLineMode(true); //Line Mode
+
+ printf("ATZ"); //Reinit
+ wait(.500);
+ //flushBuffer();
+// printf("ATE0 ^CURC=0"); //Deactivate echo & notif
+ printf("ATE0"); //Deactivate echo & notif
+ wait(.500);
+ flushBuffer();
+
+ DBG("ATZ ATE...\n");
+
+ int len = writeLine("ATQ0 V1 S0=0 &C1 &D2 +FCLASS=0");//writeLine("ATQ0 V1 S0=0 &C1 &D2 +FCLASS=0");
+ ATErr err = AT_OK;
+ if(len<0)
+ err=(ATErr)len;
+
+ if(!err)
+ {
+ err = checkOK();
+ if (err) //No ACK from module
+ {
+ DBG("Opening port, error %d.\n", err);
+ if(err==AT_TIMEOUT)
+ err = AT_NOANSWER;
+ }
+ }
+
+ if(err)
+ {
+ SerialBuf::detach();
+ m_isOpen = false;
+ return err;
+ }
+
+ DBG("No error.\n");
+ #if 0//FIXME
+ m_signalsEnable = true;
+ #endif
+ //FIXME:
+// m_pSerial->attach<ATIf>(this, &ATIf::onSerialInterrupt);
+
+ return AT_OK;
+}
+#endif
+
+ATErr ATIf::close() //Release port
+{
+ SerialBuf::detach(); //Detach serial buf
+ m_isOpen = false;
+ //m_signalsEnable = false;
+ return AT_OK;
+}
+
+ATErr ATIf::flushBuffer()
+{
+ if(!m_isOpen)
+ return AT_CLOSED;
+
+ int len=0;
+ //char c;
+ while(readable())
+ {
+ //DBG("Readable\n");
+ /*c =*/ getc();
+ //DBG("\r\n[%c] discarded.", c);
+ // wait(0.01);
+ len++;
+ }
+
+ DBG("\r\n%d chars discarded.", len);
+
+ return AT_OK;
+}
+
+ATErr ATIf::flushLine(int timeout)
+{
+ if(!m_isOpen)
+ return AT_CLOSED;
+
+ Timer timer;
+
+ timer.start();
+
+ int len=0;
+ char c=0;
+ while(true)
+ {
+ while(!readable())
+ { if(timer.read_ms()>timeout)
+ {
+ // DBG("Timeout!!0");
+ return AT_TIMEOUT;
+ }
+ }
+ if(c=='\x0D')
+ {
+ c = getc();
+ len++;
+ if(c=='\x0A')
+ break;
+ }
+ else
+ {
+ c = getc();
+ len++;
+ }
+ }
+
+// DBG("\r\n%d chars discarded.", len);
+
+ return AT_OK;
+}
+
+#if 0
+bool ATIf::onRead()
+{
+ if(!m_signalsEnable)
+ return false;
+
+ //Save Usermode params
+ volatile int u_readTimeout = m_readTimeout;
+ volatile bool u_lineMode = m_lineMode;
+// bool u_isOpen = m_isOpen;
+ SerialBuf::setReadMode(true);
+
+ m_readTimeout = 0; //No timeout in an interrupt fn!
+
+ bool handled;
+ if(!!flushLine(0))
+ {
+ SerialBuf::resetRead();
+ //Not a complete line here, wait...
+ handled = false;
+ }
+ else
+ {
+ SerialBuf::resetRead();
+ handled = true;
+ if( handleSignal() ) //Was that a signal ?
+ {
+ //OK, discard data since it has been processed
+ SerialBuf::flushRead();
+ }
+ else
+ {
+ //Keep data since it has not been processed yet
+ //Have to be processed in usermode
+ SerialBuf::resetRead();
+// handled = false;
+ }
+ }
+ //Restore Usermode params
+ m_readTimeout = u_readTimeout;
+ m_lineMode = u_lineMode;
+ //m_isOpen = u_isOpen;
+ return handled;
+}
+#endif
+
+ATErr ATIf::rawOpen(Serial* pSerial, int baudrate) //Simple open function for similar non-conforming protocols
+{
+ DBG("\r\nOpening...");
+ m_isOpen = true; //Must be set so that the serial port-related fns work
+ //Setup options
+ pSerial->baud(baudrate);
+ SerialBuf::attach(pSerial);
+
+ return AT_OK;
+}
+
+#if 0
+ATErr ATIf::command(const char* cmd, char* result, int resultLen, int timeout) ////WARN/FIXME: result has to be long enough!!!
+{
+ if(!m_isOpen)
+ return AT_CLOSED;
+
+ flushBuffer();
+
+ int err;
+ err = writeLine(cmd);
+
+ if(err<0)
+ { m_receiveStatus = AT_READY; return (ATErr)err; }
+
+ err = readLine(result, resultLen, timeout);
+
+ if(err<0)
+ { m_receiveStatus = AT_READY; return (ATErr)err; }
+
+ m_receiveStatus = AT_READY;
+
+ return AT_OK;
+
+}
+#endif
+
+ATErr ATIf::write(const char* cmd, bool lineMode /*= false*/)
+{
+ if(!m_isOpen)
+ return AT_CLOSED;
+
+ int err;
+ err = lineMode ? writeLine(cmd) : writeRaw(cmd);
+
+ if(err<0)
+ return (ATErr)err;
+
+ return AT_OK;
+}
+
+
+ATErr ATIf::read(char* result, int resultMaxLen, int timeout, bool lineMode /*= false*/, int resultMinLen/* = 0*/)
+{
+ if(!m_isOpen)
+ return AT_CLOSED;
+
+ int err;
+ err = lineMode ? readLine(result, resultMaxLen, timeout) : readRaw(result, resultMaxLen, timeout, resultMinLen);
+
+ if(err<0)
+ return (ATErr)err;
+
+ return AT_OK;
+}
+
+bool ATIf::isOpen()
+{
+ return m_isOpen;
+}
+
+ATErr ATIf::checkOK() //Helper fn to quickly check that OK has been returned
+{
+ char ret[16] = {0};
+ int err = readLine(ret,16,m_readTimeout);
+
+ if(err<0)
+ {
+ DBG("\r\nError in check (%s).\r\n", ret);
+ flushBuffer(); //Discard anything in buf to avoid misparsing in the following calls
+ return (ATErr)err;
+ }
+
+ if(!!strcmp("OK",ret))
+ {
+ DBG("\r\nNot an OK <%s>.\r\n", ret);
+ flushBuffer();
+ return AT_ERROR;
+ }
+
+ DBG("\r\nCHECK OK\r\n");
+
+ return AT_OK;
+}
+
+#if 0
+void ATIf::onSerialInterrupt() //Callback from m_pSerial
+{
+return;//FIXME
+
+ if(m_receiveStatus == AT_READING)
+ return;
+
+ if( m_cbObj && m_cbMeth )
+ return (m_cbObj->*m_cbMeth)();
+}
+#endif
+
+int ATIf::readLine(char* line, int maxLen, int timeout) //Read a single line from serial port, return length or ATErr(<0)
+{
+#ifdef OLDREADLINE
+ if(!m_isOpen)
+ return AT_CLOSED;
+
+ int len = 0;
+
+ Timer timer;
+
+ timer.start();
+#ifdef __START_CLRF_MANDAT
+ for( int i=0; i<2; i++ )
+ {
+ while(!readable())
+ {
+ if(timer.read_ms()>timeout)
+ {
+// DBG("Timeout!!0");
+ return AT_TIMEOUT;
+ }
+ wait_ms(10); //Wait 10ms
+ }
+ *line = getc();
+ // DBG("In readLine(), read : %c", *line);
+ if( ( (i == 0) && (*line!='\x0D') )
+ || ( (i == 1) && (*line!='\x0A') ) )
+ return AT_PARSE;
+ }
+#else
+
+#endif
+
+ for( ; len < maxLen ; len++ )
+ {
+ timer.reset();
+ while(!readable())
+ {
+ if(timer.read_ms()>timeout)
+ {
+// DBG("Timeout!!1");
+ return AT_TIMEOUT;
+ }
+ wait_ms(10); //Wait 10ms
+ }
+ *line = getc();
+ //DBG("In readLine(), read : %c", *line);
+
+ if(*line=='\x0D')
+ {
+ timer.reset();
+ while(!readable())
+ {
+ if(timer.read_ms()>timeout)
+ {
+ return AT_TIMEOUT;
+ }
+ wait_ms(10); //Wait 1ms
+ }
+ *line = getc();
+ // DBG("In readLine(), read : %c", *line);
+ if(*line=='\x0A')
+ {
+ if(len==0)
+ {
+ //Start of line
+ len--;
+ continue;
+ }
+ else
+ {
+ *line=0; //End of line
+ break;
+ }
+ }
+ else
+ {
+ //Should not happen, must have lost some bytes somewhere or non AT protocol
+ return AT_PARSE;
+ }
+ }
+ line++;
+ }
+
+ if(len==maxLen)
+ return AT_INCOMPLETE; //Buffer full, must call this method again to get end of line
+
+ return len;
+#else
+ if(!m_isOpen)
+ return AT_CLOSED;
+
+ Timer timer;
+ timer.start();
+
+ int len = 0;
+ while( len < maxLen )
+ {
+ timer.reset();
+ while(!readable())
+ {
+ if(timer.read_ms()>timeout)
+ {
+ return AT_TIMEOUT;
+ }
+ wait_ms(10); //Wait 10ms
+ }
+ *line = getc();
+
+ if( (*line=='\x0D') || (*line=='\x0A') )
+ {
+
+ if(len==0)
+ {
+ //Start of line
+ continue;
+ }
+ else
+ {
+ *line=0; //End of line
+ break;
+ }
+ }
+ len++;
+ line++;
+ }
+
+ if(len==maxLen)
+ return AT_INCOMPLETE; //Buffer full, must call this method again to get end of line
+
+ return len;
+#endif
+}
+
+int ATIf::writeLine(const char* line) //Write a single line to serial port
+{
+// char* line = (char*) _line;
+ if(!m_isOpen)
+ return AT_CLOSED;
+
+// DBG("\n\rIn writeline.");
+
+ int len = 0;
+
+ while(*line)
+ {
+ putc(*line);
+ line++;
+ len++;
+ }
+
+ /* putc('\r');
+
+ putc('\n');*/
+
+ putc('\x0D');
+// putc('\x0A');
+
+// DBG("\n\rWritten %d + 1", len);
+
+ return len;
+
+}
+
+
+
+int ATIf::readRaw(char* str, int maxLen, int timeout /*= 0*/, int minLen /*= 0*/) //Read from serial port in buf
+{
+ if(!m_isOpen)
+ return AT_CLOSED;
+
+ int len = 0;
+
+ Timer timer;
+
+ timer.start();
+
+ for( ; len < maxLen ; len++ )
+ {
+ while( (len < minLen) && !readable())
+ {
+ if(timer.read_ms()>timeout)
+ {
+ return AT_TIMEOUT;
+ }
+ wait(.01); //Wait 10ms
+ }
+
+ if(!readable()) //Buffer read entirely
+ break;
+
+ *str = getc();
+ str++;
+ len++;
+ }
+
+ *str = 0; //End char
+
+ return len;
+
+}
+
+int ATIf::writeRaw(const char* str) //Write directly to serial port
+{
+ if(!m_isOpen)
+ return AT_CLOSED;
+
+ int len = 0;
+
+ while(*str)
+ {
+ putc(*str);
+ str++;
+ len++;
+ }
+
+ return len;
+}
+
+#if 0
+bool ATIf::handleSignal()
+{
+ bool beg = false;
+
+// SerialBuf::setReadMode(true); //Keep chars in buf when read
+// SerialBuf::resetRead();
+
+ //if( !m_pCurrentSignal ) //If no signal asked for this line
+ if(true) //Check anyway, could have been some parsing error before
+ {
+ //Extract Signal Name
+ char sigName[32]; //Should not be longer than that
+ setLineMode(true); //Read one line
+
+ int len = scanf("%[^:]:%*[^\n]", sigName);
+ if(len != 1)
+ return false; //This is not a signal
+ // DBG("\r\nGot signal %s\r\n", sigName);
+
+ list<ATSigHandler>::iterator it;
+
+ for ( it = m_signals.begin(); it != m_signals.end(); it++ )
+ {
+ if( !strcmp((*it).m_name, sigName) )
+ {
+ // DBG("\r\nFound signal %s\r\n", sigName);
+ m_pCurrentSignal = &(*it);
+ beg = true;
+ break;
+ }
+ }
+
+
+ }
+
+ if( !m_pCurrentSignal )
+ return false; //This is not a signal or it cannot be handled
+
+ bool moreData = false;
+ //Call signal handling routine
+ SerialBuf::resetRead(); //Rollback so that the handling fn can call scanf properly
+ bool result = ((m_pCurrentSignal->m_cbObj)->*(m_pCurrentSignal->m_cbMeth))(this, beg, &moreData);
+
+ if( !moreData ) //Processing completed
+ {
+ m_pCurrentSignal = NULL;
+ }
+
+ return result;
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/at/ATIf.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,130 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef ATIF_H
+#define ATIF_H
+
+#include "netCfg.h"
+
+#include "mbed.h"
+#include "drv/serial/buf/SerialBuf.h"
+#include <list>
+using std::list;
+
+//class Serial; //Pb w forward decl
+
+enum ATErr
+{
+ __AT_MIN = -0xFFFF,
+ AT_CLOSED,
+ AT_NOANSWER,
+ AT_ERROR,
+ AT_TIMEOUT,
+ AT_BUSY,
+ AT_PARSE,
+ AT_INCOMPLETE,
+ AT_OK = 0
+};
+
+class ATIf : public SerialBuf
+{
+ public:
+
+ ATIf();
+ virtual ~ATIf();
+
+#if 0
+ template<class T>
+ //void attachSignal( const char* sigName, T* pItem, bool (T::*pMethod)(ATIf*, bool, bool*) ); //Attach Signal ("Unsollicited response code" in Telit_AT_Reference_Guide.pdf) to an handler fn
+ //Linker bug : Must be defined here :(
+ void attachSignal( const char* sigName, T* pItem, bool (T::*pMethod)(ATIf*, bool, bool*) ) //Attach Signal ("Unsollicited response code" in Telit_AT_Reference_Guide.pdf) to an handler fn
+ {
+ ATSigHandler sig(sigName, (ATSigHandler::CDummy*)pItem, (bool (ATSigHandler::CDummy::*)(ATIf*, bool, bool*))pMethod);
+ m_signals.push_back(sig);
+ }
+ void detachSignal( const char* sigName );
+#endif
+
+ ATErr open(Serial* pSerial); //Deactivate echo, etc
+ #if NET_USB_SERIAL
+ ATErr open(UsbSerial* pUsbSerial); //Deactivate echo, etc
+ #endif
+ ATErr close(); //Release port
+
+ int printf(const char* format, ... );
+ int scanf(const char* format, ... );
+ void setTimeout(int timeout); //used by scanf
+ void setLineMode(bool lineMode); //Switch btw line & raw fns
+ //void setSignals(bool signalsEnable);
+ ATErr flushBuffer(); //Discard input buffer
+ ATErr flushLine(int timeout); //Discard input buffer until CRLF is encountered
+
+ protected:
+ //virtual bool onRead(); //Inherited from SerialBuf, return true if data is incomplete
+ ATErr rawOpen(Serial* pSerial, int baudrate); //Simple open function for similar non-conforming protocols
+
+ public:
+/* ATErr command(const char* cmd, char* result, int resultLen, int timeout); */ //Kinda useless
+ ATErr write(const char* cmd, bool lineMode = false);
+ ATErr read(char* result, int resultMaxLen, int timeout, bool lineMode = false, int resultMinLen = 0);
+ bool isOpen();
+ ATErr checkOK(); //Helper fn to quickly check that OK has been returned
+
+ private:
+ int readLine(char* line, int maxLen, int timeout); //Read a single line from serial port
+ int writeLine(const char* line); //Write a single line to serial port
+
+ int readRaw(char* str, int maxLen, int timeout = 0, int minLen = 0); //Read from serial port in buf
+ int writeRaw(const char* str); //Write directly to serial port
+
+ volatile int m_readTimeout;
+ volatile bool m_lineMode;
+ //bool m_signalsEnable;
+ bool m_isOpen;
+
+ char* m_tmpBuf;
+
+#if 0
+ class ATSigHandler
+ {
+ class CDummy;
+ public:
+ ATSigHandler(const char* name, CDummy* cbObj, bool (CDummy::*cbMeth)(ATIf* pIf, bool beg, bool* pMoreData)) : m_cbObj(cbObj), m_cbMeth(cbMeth), m_name(name)
+ {}
+ protected:
+ CDummy* m_cbObj;
+ bool (CDummy::*m_cbMeth)(ATIf* pIf, bool beg, bool* pMoreData); //*pMoreData set to true if needs to read next line, beg = true if beginning of new code
+ const char* m_name;
+
+ friend class ATIf;
+ };
+
+ volatile ATSigHandler* m_pCurrentSignal; //Signal that asked more data
+
+ bool handleSignal(); //Returns true if signal has been handled
+ list<ATSigHandler> m_signals;
+#endif
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/eth/eth_drv.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,212 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "netCfg.h"
+#if NET_ETH
+
+#include "mbed.h"
+
+Ethernet *pEth = NULL;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+#include "netif/etharp.h"
+#include "string.h"
+
+//#include "eth_drv.h"
+
+#define IFNAME0 'E'
+#define IFNAME1 'X'
+
+#define min(x,y) (((x)<(y))?(x):(y))
+
+struct netif* eth_netif;
+
+static err_t eth_output(struct netif *netif, struct pbuf *p) {
+ #if ETH_PAD_SIZE
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+ #endif
+
+ do {
+ pEth->write((const char *)p->payload, p->len);
+ } while((p = p->next)!=NULL);
+
+ pEth->send();
+
+ #if ETH_PAD_SIZE
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
+ #endif
+
+ LINK_STATS_INC(link.xmit);
+ return ERR_OK;
+}
+
+/*
+void show(char *buf, int size) {
+ printf("Destination: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+ printf("Source: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
+ buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]);
+
+ printf("Type %hd\n", htons((short)buf[12]));
+
+ // hexview(buf, size);
+}
+*/
+
+void eth_poll() {
+ struct eth_hdr *ethhdr;
+ struct pbuf *frame, *p;
+ int len, read;
+
+ while((len = pEth->receive()) != 0) {
+ frame = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+ if(frame == NULL) {
+ return;
+ }
+ p = frame;
+ /* no packet could be read, silently ignore this */
+ if (p == NULL) return;
+ do {
+ read = pEth->read((char *)p->payload, p->len);
+ p = p->next;
+ } while(p != NULL && read != 0);
+
+ #if ETH_PAD_SIZE
+ pbuf_header(p, ETH_PAD_SIZE);
+ #endif
+
+ ethhdr = (struct eth_hdr *)(frame->payload);
+
+ // show((char*)ethhdr, 13);
+
+ /*
+ switch(htons(ethhdr->type)) {
+
+ case ETHTYPE_IP:
+ etharp_ip_input(gnetif, frame);
+ pbuf_header(frame, -((s16_t) sizeof(struct eth_hdr)));
+ gnetif->input(frame, gnetif);
+ break;
+
+ case ETHTYPE_ARP:
+ etharp_arp_input(gnetif, (struct eth_addr *)(gnetif->hwaddr), frame);
+ break;
+
+ default:
+ break;
+ }*/
+
+
+
+ //ethernet_input(frame, gnetif);
+
+ switch (htons(ethhdr->type)) {
+ /* IP or ARP packet? */
+ case ETHTYPE_IP:
+ case ETHTYPE_ARP:
+ #if PPPOE_SUPPORT
+ /* PPPoE packet? */
+ case ETHTYPE_PPPOEDISC:
+ case ETHTYPE_PPPOE:
+ #endif /* PPPOE_SUPPORT */
+ /* full packet send to tcpip_thread to process */
+ //if (netif->input(p, gnetif)!=ERR_OK)
+ if (ethernet_input(frame, eth_netif)!=ERR_OK)
+ { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
+ pbuf_free(frame);
+ frame = NULL;
+ }
+ break;
+
+ default:
+ pbuf_free(frame);
+ frame = NULL;
+ break;
+ }
+
+ /* pbuf_free(frame); */
+ }
+
+
+
+
+}
+
+err_t eth_init(struct netif *netif) {
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+
+ NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 0x2EA);
+
+ /* maximum transfer unit */
+ netif->mtu = 0x2EA;
+
+ /* device capabilities */
+ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_IGMP;
+
+ netif->state = NULL;
+ eth_netif = netif;
+
+ netif->name[0] = IFNAME0;
+ netif->name[1] = IFNAME1;
+
+ /* We directly use etharp_output() here to save a function call.
+ * You can instead declare your own function an call etharp_output()
+ * from it if you have to do some checks before sending (e.g. if link
+ * is available...) */
+ netif->output = etharp_output;
+ netif->linkoutput = eth_output;
+
+ if (!pEth) pEth = new Ethernet(); // only create Ethernet object if required
+
+ return ERR_OK;
+}
+
+void eth_free()
+{
+ if(pEth)
+ delete pEth;
+ pEth = NULL;
+}
+
+void eth_address(char* mac) {
+ pEth->address(mac);
+}
+
+Ethernet* eth_interface() {
+ return pEth;
+}
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/eth/eth_drv.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,41 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef ETHDRV_H
+#define ETHDRV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void eth_poll();
+err_t eth_init(struct netif* netif);
+void eth_address(char* mac);
+void eth_free();
+Ethernet* eth_interface();
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/gprs/GPRSModem.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,292 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "GPRSModem.h"
+#include "mbed.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+#define WAIT_BTW_NETW_POLLS 3.
+
+#include "netCfg.h"
+#if NET_GPRS
+
+GPRSModem::GPRSModem() : ATIf()
+{
+ DBG("New GPRSModem@%p\n", this);
+}
+
+GPRSModem::~GPRSModem()
+{
+
+}
+
+GPRSErr GPRSModem::getNetworkState()
+{
+ ATIf::flushBuffer();
+ /*
+ netState can be : (Telit_AT_Reference_Guide.pdf p.98)
+ 0 - not registered, ME is not currently searching a new operator to register to
+ 1 - registered, home network
+ 2 - not registered, but ME is currently searching a new operator to register to
+ 3 - registration denied
+ 4 - unknown
+ 5 - registered, roaming
+ */
+ // DBG("Network?...\r\n");
+ ATIf::setReadMode(false); //Discard chars
+ ATIf::setTimeout(10000);
+ ATIf::setLineMode(true); //Line mode
+ int netState = 0;
+ int len;
+ len = ATIf::printf("AT+CREG?"); //Registered ?
+ if(!len) DBG("\r\nprintf - len=%d\r\n",len);
+ if(!len)
+ return GPRS_MODEM; //Nothing was actually sent
+
+ len = ATIf::scanf("+CREG: 0,%d", &netState); //Get status
+ if(len != 1) DBG("\r\nscanf - len=%d\r\n",len);
+ if(len != 1) //Likely +CMS ERROR was returned
+ return GPRS_MODEM;
+
+ if( !!ATIf::checkOK() ) //Should not be a problem
+ {DBG("\r\nNOK\r\n"); return GPRS_MODEM; }
+
+ switch(netState)
+ {
+ case 1:
+ case 5: //TODO: Option allow roaming
+ DBG("\r\nNetwork is up!\r\n");
+ return GPRS_OK;
+ case 3:
+ DBG("\r\nAccess to network denied.\r\n");
+ return GPRS_DENIED;
+ case 0:
+ DBG("\r\nNo network.\r\n");
+ return GPRS_NONETWORK;
+ case 4:
+ case 2:
+ //DBG("\r\nRegistering...\r\n");
+ return GPRS_REGISTERING;
+ }
+
+ return GPRS_MODEM; // Should not reach this
+
+}
+
+GPRSErr GPRSModem::setNetworkUp()
+{
+ ATIf::flushBuffer();
+ GPRSErr err = GPRS_REGISTERING;
+ while(true)
+ {
+ err = getNetworkState();
+ if(err != GPRS_REGISTERING)
+ break;
+ wait(WAIT_BTW_NETW_POLLS);
+ }
+ return err;
+}
+
+//Same, but for GPRS
+GPRSErr GPRSModem::getGPRSState()
+{
+ ATIf::flushBuffer();
+ /*
+ netState can be : (Telit_AT_Reference_Guide.pdf p.192)
+ 0 - not registered, terminal is not currently searching a new operator to register to
+ 1 - registered, home network
+ 2 - not registered, but terminal is currently searching a new operator to register to
+ 3 - registration denied
+ 4 - unknown
+ 5 - registered, roaming
+ */
+
+ DBG("GPRS?...\r\n");
+ ATIf::setReadMode(false); //Discard chars
+ ATIf::setTimeout(10000);
+ ATIf::setLineMode(true); //Line mode
+ int netState = 0;
+ int len;
+ len = ATIf::printf("AT+CGREG?"); //Registered ?
+ if(!len)
+ return GPRS_MODEM; //Nothing was actually sent
+
+ len = ATIf::scanf("+CGREG: %*d,%d", &netState); //Get GPRS status, see GSM 07.07 spec as Telit AT ref is wrong
+ if(len != 1) DBG("\r\nscanf - len=%d\r\n",len);
+ if(len != 1) //Likely +CMS ERROR was returned
+ return GPRS_MODEM;
+
+ if( !!ATIf::checkOK() ) //Should not be a problem
+ return GPRS_MODEM;
+
+ switch(netState)
+ {
+ case 1:
+ case 5: //TODO: Option allow roaming
+ DBG("\r\nNetwork is up!\r\n");
+ return GPRS_OK;
+ case 3:
+ DBG("\r\nAccess to network denied.\r\n");
+ return GPRS_DENIED;
+ case 0:
+ DBG("\r\nNo network.\r\n");
+ return GPRS_NONETWORK;
+ case 4:
+ case 2:
+ DBG("\r\nRegistering...\r\n");
+ return GPRS_REGISTERING;
+ }
+
+ return GPRS_MODEM; // Should not reach this
+
+}
+
+GPRSErr GPRSModem::setGPRSUp()
+{
+ ATIf::flushBuffer();
+ GPRSErr err;
+
+ err = setNetworkUp();
+ if(err)
+ return err;
+
+ DBG("\r\nAttaching GPRS...\r\n");
+ ATIf::setReadMode(false); //Discard chars
+ ATIf::setTimeout(10000);
+ ATIf::setLineMode(true); //Line mode
+ int len;
+
+ err = getGPRSState();
+ if(err == GPRS_NONETWORK)
+ {
+ len = ATIf::printf("AT+CGATT=1"); //Attach
+ if(!len)
+ return GPRS_MODEM; //Nothing was actually sent
+
+ if( !!ATIf::checkOK() ) //Should not be a problem
+ return GPRS_MODEM;
+ }
+
+ while(true)
+ {
+ err = getGPRSState();
+ if(err != GPRS_REGISTERING)
+ break;
+ wait(WAIT_BTW_NETW_POLLS);
+ }
+ return err;
+}
+
+GPRSErr GPRSModem::setGPRSDown()
+{
+ ATIf::flushBuffer();
+ DBG("\r\nDetaching GPRS...\r\n");
+ ATIf::setReadMode(false); //Discard chars
+ ATIf::setTimeout(10000);
+ ATIf::setLineMode(true); //Line mode
+ int len;
+
+ len = ATIf::printf("AT+CGATT=0"); //Detach
+ if(!len)
+ return GPRS_MODEM; //Nothing was actually sent
+
+ if( !!ATIf::checkOK() ) //Should not be a problem
+ return GPRS_MODEM;
+
+ return GPRS_OK;
+}
+
+
+GPRSErr GPRSModem::connect(const char* apn /*=NULL*/)
+{
+ ATIf::flushBuffer();
+ GPRSErr err;
+
+ ATIf::setReadMode(false); //Discard chars
+ ATIf::setTimeout(5000);
+ ATIf::setLineMode(true); //Line mode
+
+ DBG("\r\nConnecting...\r\n");
+
+ int len;
+
+ if( apn != NULL ) //Config APN
+ {
+ len = ATIf::printf("AT+CGDCONT=1,\"IP\",\"%s\"",apn); //Define APN
+ if(!len)
+ return GPRS_MODEM; //Nothing was actually sent
+
+ if( !!ATIf::checkOK() ) //Should not be a problem
+ return GPRS_MODEM;
+ }
+
+ err = setGPRSUp();
+ if(err)
+ return err;
+
+ ATIf::setReadMode(false); //Discard chars
+ ATIf::setTimeout(60000);
+ ATIf::setLineMode(true); //Line mode
+ //
+ //len = ATIf::printf("AT+CGDATA=\"PPP\",1"); //Connect using PDP context #1
+// len = ATIf::printf("ATDT *99***1#");
+ len = ATIf::printf("ATDT *99#");
+ if(!len)
+ return GPRS_MODEM; //Nothing was actually sent
+
+ len = ATIf::scanf("CONNECT"); //Beginning of session
+ if(len != 0) //Likely +CME ERROR was returned or NO CARRIER
+ return GPRS_MODEM;
+
+ //ATIf::setSignals(false);
+
+ DBG("\r\nConnected.\r\n");
+
+ return GPRS_OK; //Time to enter a PPP Session !
+
+}
+
+GPRSErr GPRSModem::disconnect()
+{
+ ATIf::flushBuffer();
+ ATIf::setReadMode(false); //Discard chars
+ ATIf::setTimeout(5000);
+ ATIf::setLineMode(true); //Line mode
+
+ if( !!ATIf::checkOK() ) //Should be present at the end of connection
+ return GPRS_MODEM;
+
+ GPRSErr err;
+ err = setGPRSDown();
+ if(err)
+ return err;
+
+ DBG("\r\nDisconnected.\r\n");
+
+ return GPRS_OK;
+}
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/gprs/GPRSModem.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,57 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef GPRSMODEM_H
+#define GPRSMODEM_H
+
+#include "drv/at/ATIf.h"
+
+enum GPRSErr
+{
+ __GPRS_MIN = -0xFFFF,
+ GPRS_MODEM, //ATErr returned
+ GPRS_DENIED,
+ GPRS_NONETWORK,
+ GPRS_REGISTERING,
+ GPRS_OK = 0
+};
+
+class GPRSModem : public ATIf
+{
+public:
+ GPRSModem();
+ virtual ~GPRSModem();
+
+ GPRSErr getNetworkState();
+ GPRSErr setNetworkUp();
+
+ GPRSErr getGPRSState();
+ GPRSErr setGPRSUp();
+ GPRSErr setGPRSDown();
+
+ GPRSErr connect(const char* apn = NULL);
+ GPRSErr disconnect();
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/gprsmodule/TelitModule.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,100 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "TelitModule.h"
+
+#include "mbed.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+#include "netCfg.h"
+#if NET_GPRS_MODULE
+
+TelitModule::TelitModule(PinName pwrSetPin, PinName pwrMonPin) : m_pwrSetPin(pwrSetPin), m_pwrMonPin(pwrMonPin)
+{
+ //m_pwrSetPin.write(0);
+
+ //m_pwrMonPin.mode(PullDown);
+}
+
+TelitModule::~TelitModule() {
+
+}
+
+bool TelitModule::isOn() //True if on
+{
+ return m_pwrMonPin.read();
+}
+
+bool TelitModule::on() //True if OK
+{
+ Timer tmr;
+ if(!m_pwrMonPin.read()){
+ //On
+ DBG("Switching On...\n");
+ m_pwrSetPin.write(1);
+ wait(1.);
+ m_pwrSetPin.write(0);
+
+ tmr.start();
+ while(!m_pwrMonPin.read())
+ {
+ wait(.001);
+ if(tmr.read() > 2)
+ {
+ DBG("ERROR - MUST RESET MODULE\n");
+ break;
+ }
+ }
+ tmr.stop();
+ }
+ wait(.1);
+ return m_pwrMonPin.read();
+}
+
+bool TelitModule::off() { //True if OK
+ Timer tmr;
+ if(m_pwrMonPin.read()){
+ //Off
+ DBG("Switching Off...\n");
+ m_pwrSetPin.write(1);
+ wait(3.);
+ m_pwrSetPin.write(0);
+ DBG("Waiting....\n");
+ tmr.start();
+ while(m_pwrMonPin.read())
+ {
+ wait(.001);
+ if(tmr.read() > 15)
+ {
+ DBG("ERROR - MUST RESET MODULE\n");
+ break;
+ }
+ }
+ tmr.stop();
+ }
+ return !m_pwrMonPin.read();
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/gprsmodule/TelitModule.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,47 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef TELITMODULE_H
+#define TELITMODULE_H
+
+#include "mbed.h"
+
+class TelitModule
+{
+public:
+ TelitModule(PinName pwrSetPin, PinName pwrMonPin);
+ ~TelitModule();
+
+ bool isOn(); //True if on
+ bool on(); //True if OK
+ bool off(); //True if OK
+
+protected:
+
+private:
+ DigitalOut m_pwrSetPin;
+ DigitalIn m_pwrMonPin;
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/serial/buf/SerialBuf.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,235 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "SerialBuf.h"
+#include "mbed.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+#include "netCfg.h"
+#if NET_GPRS
+
+#if NET_USB_SERIAL
+#define m_pStream( a ) (m_pSerial?m_pSerial->a:m_pUsbSerial->a)
+#else
+#define m_pStream( a ) (m_pSerial->a)
+#endif
+
+//Circular buf
+
+SerialCircularBuf::SerialCircularBuf(int len) : m_readMode(false)
+{
+ m_buf = new char[len];
+ m_len = len;
+ m_pReadStart = m_pRead = m_buf;
+ m_pWrite = m_buf;
+}
+
+SerialCircularBuf::~SerialCircularBuf()
+{
+ if(m_buf)
+ delete[] m_buf;
+}
+
+int SerialCircularBuf::room() //Return room available in buf
+{
+ //return m_len - len() - 1; //-1 is to avoid loop
+ if ( m_pReadStart > m_pWrite )
+ return ( m_pReadStart - m_pWrite - 1 );
+ else
+ return m_len - ( m_pWrite - m_pReadStart ) - 1;
+}
+
+int SerialCircularBuf::len() //Return chars length in buf
+{
+ if ( m_pWrite >= m_pRead )
+ return ( m_pWrite - m_pRead );
+ else
+ return m_len - ( m_pRead - m_pWrite ); // = ( ( m_buf + m_len) - m_pRead ) + ( m_pWrite - m_buf )
+}
+
+void SerialCircularBuf::write(char c)
+{
+#if 0
+ if(!room())
+ return;
+#endif
+ //WARN: Must call room() before
+ *m_pWrite = c;
+ m_pWrite++;
+ if(m_pWrite>=m_buf+m_len)
+ m_pWrite=m_buf;
+}
+char SerialCircularBuf::read()
+{
+#if 0
+ if(!len())
+ return 0;
+#endif
+ //WARN: Must call len() before
+ char c = *m_pRead;
+ m_pRead++;
+
+ if(m_pRead>=m_buf+m_len)
+ m_pRead=m_buf;
+
+ if(!m_readMode) //If readmode=false, trash this char
+ m_pReadStart=m_pRead;
+
+ return c;
+}
+
+void SerialCircularBuf::setReadMode(bool readMode) //If true, keeps chars in buf when read, false by default
+{
+ if(m_readMode == true && readMode == false)
+ {
+ //Trash bytes that have been read
+ flushRead();
+ }
+ m_readMode = readMode;
+}
+
+void SerialCircularBuf::flushRead() //Delete chars that have been read & return chars len (only useful with readMode = true)
+{
+ m_pReadStart = m_pRead;
+}
+
+void SerialCircularBuf::resetRead() //Go back to initial read position & return chars len (only useful with readMode = true)
+{
+ m_pRead = m_pReadStart;
+}
+
+//SerialBuf
+
+SerialBuf::SerialBuf(int len) : m_rxBuf(len), m_txBuf(len), m_pSerial(NULL) //Buffer length
+#if NET_USB_SERIAL
+, m_pUsbSerial(NULL)
+#endif
+{
+ DBG("New Serial buf@%p\n", this);
+}
+
+SerialBuf::~SerialBuf()
+{
+
+}
+
+void SerialBuf::attach(Serial* pSerial)
+{
+ DBG("Serial buf@%p in attach\n", this);
+ m_pSerial = pSerial;
+ m_pSerial->attach<SerialBuf>(this, &SerialBuf::onRxInterrupt, Serial::RxIrq);
+ m_pSerial->attach<SerialBuf>(this, &SerialBuf::onTxInterrupt, Serial::TxIrq);
+ onRxInterrupt(); //Read data
+}
+
+void SerialBuf::detach()
+{
+ if(m_pSerial)
+ {
+ m_pSerial->attach<SerialBuf>(NULL, NULL, Serial::RxIrq);
+ m_pSerial->attach<SerialBuf>(NULL, NULL, Serial::TxIrq);
+ m_pSerial = NULL;
+ }
+ #if NET_USB_SERIAL
+ else if(m_pUsbSerial)
+ {
+ m_pUsbSerial->attach<SerialBuf>(NULL, NULL, UsbSerial::RxIrq);
+ m_pUsbSerial->attach<SerialBuf>(NULL, NULL, UsbSerial::TxIrq);
+ m_pUsbSerial = NULL;
+ }
+ #endif
+}
+
+#if NET_USB_SERIAL
+void SerialBuf::attach(UsbSerial* pUsbSerial)
+{
+ m_pUsbSerial = pUsbSerial;
+ m_pUsbSerial->attach<SerialBuf>(this, &SerialBuf::onRxInterrupt, UsbSerial::RxIrq);
+ m_pUsbSerial->attach<SerialBuf>(this, &SerialBuf::onTxInterrupt, UsbSerial::TxIrq);
+ onRxInterrupt(); //Read data
+}
+#endif
+
+char SerialBuf::getc()
+{
+ while(!readable());
+ char c = m_rxBuf.read();
+ return c;
+}
+
+void SerialBuf::putc(char c)
+{
+ while(!writeable());
+ m_txBuf.write(c);
+ onTxInterrupt();
+}
+
+bool SerialBuf::readable()
+{
+ if( !m_rxBuf.len() ) //Fill buf if possible
+ onRxInterrupt();
+ return (m_rxBuf.len() > 0);
+}
+
+bool SerialBuf::writeable()
+{
+ if( !m_txBuf.room() ) //Free buf is possible
+ onTxInterrupt();
+ return (m_txBuf.room() > 0);
+}
+
+void SerialBuf::setReadMode(bool readMode) //If true, keeps chars in buf when read, false by default
+{
+ m_rxBuf.setReadMode(readMode);
+}
+
+void SerialBuf::flushRead() //Delete chars that have been read & return chars len (only useful with readMode = true)
+{
+ m_rxBuf.flushRead();
+}
+
+void SerialBuf::resetRead() //Go back to initial read position & return chars len (only useful with readMode = true)
+{
+ m_rxBuf.resetRead();
+}
+
+void SerialBuf::onRxInterrupt() //Callback from m_pSerial
+{
+ while( m_rxBuf.room() && m_pStream(readable()) )
+ {
+ m_rxBuf.write(m_pStream(getc()));
+ }
+}
+
+void SerialBuf::onTxInterrupt() //Callback from m_pSerial
+{
+ while( m_txBuf.len() && m_pStream(writeable()) )
+ {
+ m_pStream(putc(m_txBuf.read()));
+ }
+}
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/serial/buf/SerialBuf.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,99 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef SERIALBUF_H
+#define SERIALBUF_H
+
+#include "mbed.h"
+#include "netCfg.h"
+#if NET_USB_SERIAL
+#include "drv/serial/usb/UsbSerial.h"
+#endif
+
+class SerialCircularBuf
+{
+public:
+ SerialCircularBuf(int len);
+ ~SerialCircularBuf();
+
+ int room();
+ int len();
+
+ void write(char c);
+ char read();
+
+ void setReadMode(bool readMode); //If true, keeps chars in buf when read, false by default
+ void flushRead(); //Delete chars that have been read & return chars len (only useful with readMode = true)
+ void resetRead(); //Go back to initial read position & return chars len (only useful with readMode = true)
+
+private:
+ char* m_buf;
+ int m_len;
+
+ volatile char* m_pReadStart;
+ volatile char* m_pRead;
+ volatile char* m_pWrite;
+ volatile bool m_readMode;
+};
+
+class SerialBuf
+{
+public:
+ SerialBuf(int len); //Buffer length
+ virtual ~SerialBuf();
+
+ void attach(Serial* pSerial);
+ void detach();
+
+ #if NET_USB_SERIAL
+ void attach(UsbSerial* pUsbSerial);
+ #endif
+
+ //Really useful for debugging
+ char getc();
+ void putc(char c);
+ bool readable();
+ bool writeable();
+/*protected:*/
+ void setReadMode(bool readMode); //If true, keeps chars in buf when read, false by default
+ void flushRead(); //Delete chars that have been read & return chars len (only useful with readMode = true)
+ void resetRead(); //Go back to initial read position & return chars len (only useful with readMode = true)
+
+private:
+ void onRxInterrupt(); //Callback from m_pSerial
+ void onTxInterrupt(); //Callback from m_pSerial
+
+ SerialCircularBuf m_rxBuf;
+ SerialCircularBuf m_txBuf;
+
+ Serial* m_pSerial; //Not owned
+
+ #if NET_USB_SERIAL
+ //USB Serial Impl
+ UsbSerial* m_pUsbSerial; //Not owned
+ //Ticker m_usbTick;
+ #endif
+};
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/serial/lwip/sio.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,202 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+/*
+
+This is a wrapper around Serial if for lwIP (acts as a serial driver)
+
+See sio.h for functions to be implemented
+
+sio_fd_t is a void* defined type, we use it as a SerialBuf ptr
+
+
+
+*/
+
+
+#include "netCfg.h"
+#if NET_PPP
+
+//#define MAX_SERIAL_PORTS 8
+
+#include "lwip/sio.h"
+#include "mbed.h"
+//#include "sioMgr.h"
+#include "drv/serial/buf/SerialBuf.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+//extern "C" {
+
+/**
+ * Opens a serial device for communication.
+ *
+ * @param devnum device number
+ * @return handle to serial device if successful, NULL otherwise
+ */
+sio_fd_t sio_open(u8_t devnum)
+{
+#if 0
+ SerialBuf* pIf = SioMgr::getIf(devnum);
+ if(pIf == NULL)
+ return NULL;
+
+ //Got a SerialBuf* object
+ //WARN: It HAS to be initialised (instanciated + attached to a Serial obj)
+
+ return (sio_fd_t) pIf;
+ #endif
+ return NULL;
+}
+
+/**
+ * Sends a single character to the serial device.
+ *
+ * @param c character to send
+ * @param fd serial device handle
+ *
+ * @note This function will block until the character can be sent.
+ */
+void sio_send(u8_t c, sio_fd_t fd)
+{
+ SerialBuf* pIf = (SerialBuf*) fd;
+ //while(!pIf->writeable());
+ pIf->putc( (char) c );
+}
+
+/**
+ * Receives a single character from the serial device.
+ *
+ * @param fd serial device handle
+ *
+ * @note This function will block until a character is received.
+ */
+u8_t sio_recv(sio_fd_t fd)
+{
+ SerialBuf* pIf = (SerialBuf*) fd;
+ pIf->setReadMode(false);
+ while(!pIf->readable());
+ return (u8_t) pIf->getc();
+}
+
+/**
+ * Reads from the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received - may be 0 if aborted by sio_read_abort
+ *
+ * @note This function will block until data can be received. The blocking
+ * can be cancelled by calling sio_read_abort().
+ */
+static volatile bool m_abort = false;
+u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)
+{
+ u32_t recvd = 0; //bytes received
+ SerialBuf* pIf = (SerialBuf*) fd;
+ pIf->setReadMode(false);
+ while(!m_abort && len)
+ {
+ while(!pIf->readable());
+ *data = (u8_t) pIf->getc();
+ data++;
+ len--;
+ recvd++;
+ }
+ m_abort = false;
+ return recvd;
+}
+
+/**
+ * Tries to read from the serial device. Same as sio_read but returns
+ * immediately if no data is available and never blocks.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received
+ */
+u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len)
+{
+ u32_t recvd = 0; //bytes received
+ SerialBuf* pIf = (SerialBuf*) fd;
+ pIf->setReadMode(false);
+ while(len)
+ {
+ /* if(!pIf->readable())
+ {
+ wait_ms(4);
+ }*/
+ if(!pIf->readable())
+ {
+ return recvd;
+ }
+ *data = (u8_t) pIf->getc();
+ data++;
+ len--;
+ recvd++;
+ }
+ return recvd;
+}
+
+/**
+ * Writes to the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data to send
+ * @param len length (in bytes) of data to send
+ * @return number of bytes actually sent
+ *
+ * @note This function will block until all data can be sent.
+ */
+u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len)
+{
+ u32_t sent = 0; //bytes sent
+ SerialBuf* pIf = (SerialBuf*) fd;
+ while(len)
+ {
+ while(!pIf->writeable());
+ pIf->putc(*data);
+ data++;
+ len--;
+ sent++;
+ }
+ return sent; //Well, this is bound to be len if no interrupt mechanism
+}
+
+/**
+ * Aborts a blocking sio_read() call.
+ *
+ * @param fd serial device handle
+ */
+void sio_read_abort(sio_fd_t fd)
+{
+ m_abort = true;
+}
+
+//}
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/serial/lwip/sioMgr.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,49 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+#if 0 //Dep
+
+#include "sioMgr.h"
+
+
+u8_t SioMgr::m_count=0;
+SerialBuf* SioMgr::m_pSerialBufList[MAX_SERIAL_PORTS] = {NULL};
+
+u8_t SioMgr::registerSerialIf(SerialBuf* pIf)
+{
+ if(m_count==MAX_SERIAL_PORTS)
+ return 0;
+
+ m_pSerialBufList[m_count] = pIf;
+ m_count++;
+
+ return m_count; //We do not want a fd to be NULL, so return value is p+1
+}
+
+SerialBuf* SioMgr::getIf(u8_t item)
+{
+ if (!item || item > m_count)
+ return NULL;
+
+ return m_pSerialBufList[item-1];
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/serial/lwip/sioMgr.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,50 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+#if 0 //Dep
+#ifndef SIOMGR_H
+#define SIOMGR_H
+
+#include "drv/serial/buf/SerialBuf.h"
+
+typedef unsigned char u8_t;
+#define MAX_SERIAL_PORTS 8
+
+//Helps to make the link btw SerialBuf classes and lwIP fd based system
+//See sio.cpp
+
+class SioMgr
+{
+public:
+
+ static u8_t registerSerialIf(SerialBuf* pIf);
+ static SerialBuf* getIf(u8_t item);
+
+private:
+
+ static SerialBuf* m_pSerialBufList[MAX_SERIAL_PORTS];
+ static u8_t m_count;
+
+};
+
+#endif
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/serial/usb/UsbSerial.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,300 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "rpc.h"
+
+#include "UsbSerial.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+#include "netCfg.h"
+#if NET_USB_SERIAL
+
+namespace mbed {
+
+#define BUF_LEN 64
+#define FLUSH_TMOUT 100000 //US
+
+UsbSerial::UsbSerial(UsbDevice* pDevice, int epIn, int epOut, const char* name /*= NULL*/) : Stream(name), m_epIn(pDevice, epIn, true, USB_BULK, BUF_LEN), m_epOut(pDevice, epOut, false, USB_BULK, BUF_LEN),
+m_pInCbItem(NULL), m_pInCbMeth(NULL), m_pOutCbItem(NULL), m_pOutCbMeth(NULL)
+{
+ m_inBufEven = new char[BUF_LEN];
+ m_inBufOdd = new char[BUF_LEN];
+ m_pInBufPos = m_inBufUsr = m_inBufEven;
+ m_inBufTrmt = m_inBufOdd;
+
+ m_outBufEven = new char[BUF_LEN];
+ m_outBufOdd = new char[BUF_LEN];
+ m_pOutBufPos = m_outBufUsr = m_outBufEven;
+ m_outBufTrmt = m_outBufOdd;
+
+ m_inBufLen = m_outBufLen = 0;
+
+ DBG("Starting RX'ing on in ep\n");
+
+ m_timeout = false;
+
+ m_epIn.setOnCompletion(this, &UsbSerial::onEpInTransfer);
+ m_epOut.setOnCompletion(this, &UsbSerial::onEpOutTransfer);
+
+ startRx();
+}
+
+UsbSerial::~UsbSerial()
+{
+ delete[] m_inBufEven;
+ delete[] m_inBufOdd;
+ delete[] m_outBufEven;
+ delete[] m_outBufOdd;
+}
+
+void UsbSerial::baud(int baudrate) {
+ //
+}
+
+void UsbSerial::format(int bits, int parity, int stop) {
+ //
+}
+
+#if 0 //For doc only
+template <class T>
+void attach(T* pCbItem, void (T::*pCbMeth)())
+{
+ m_pCbItem = (CDummy*) pCbItem;
+ m_pCbMeth = (void (CDummy::*)()) pCbMeth;
+}
+#endif
+
+int UsbSerial::_getc() {
+ NVIC_DisableIRQ(US_TICKER_TIMER_IRQn);
+ NVIC_DisableIRQ(USB_IRQn);
+ char c;
+ c = *m_pInBufPos;
+ m_pInBufPos++;
+ NVIC_EnableIRQ(USB_IRQn);
+ NVIC_EnableIRQ(US_TICKER_TIMER_IRQn);
+ return c;
+}
+
+int UsbSerial::_putc(int c) {
+ NVIC_DisableIRQ(US_TICKER_TIMER_IRQn);
+ NVIC_DisableIRQ(USB_IRQn);
+ if( (m_pOutBufPos - m_outBufUsr) < BUF_LEN )
+ {
+ *m_pOutBufPos = (char) c;
+ m_pOutBufPos++;
+ }
+ else
+ {
+ DBG("NO WAY!!!\n");
+ }
+ #if 1
+ if( (m_pOutBufPos - m_outBufUsr) >= BUF_LEN ) //Must flush
+ {
+ if(m_timeout)
+ m_txTimeout.detach();
+ startTx();
+ }
+ else
+ {
+ /*if(m_timeout)
+ m_txTimeout.detach();
+ m_timeout = true;
+ m_txTimeout.attach_us(this, &UsbSerial::startTx, FLUSH_TMOUT);*/
+ if(!m_timeout)
+ {
+ m_timeout = true;
+ m_txTimeout.attach_us(this, &UsbSerial::startTx, FLUSH_TMOUT);
+ }
+ }
+ #endif
+ //startTx();
+ NVIC_EnableIRQ(USB_IRQn);
+ NVIC_EnableIRQ(US_TICKER_TIMER_IRQn);
+ return c;
+}
+
+int UsbSerial::readable() {
+ NVIC_DisableIRQ(US_TICKER_TIMER_IRQn);
+ NVIC_DisableIRQ(USB_IRQn);
+ int res;
+ if( (m_pInBufPos - m_inBufUsr) < m_inBufLen )
+ {
+ //DBG("\r\nREADABLE\r\n");
+ res = true;
+ }
+ else
+ {
+ //DBG("\r\nNOT READABLE\r\n");
+ startRx(); //Try to swap packets & start another transmission
+ res = ((m_pInBufPos - m_inBufUsr) < m_inBufLen )?true:false;
+ }
+ NVIC_EnableIRQ(USB_IRQn);
+ NVIC_EnableIRQ(US_TICKER_TIMER_IRQn);
+ return (bool)res;
+}
+
+int UsbSerial::writeable() {
+ NVIC_DisableIRQ(US_TICKER_TIMER_IRQn);
+ NVIC_DisableIRQ(USB_IRQn);
+ // DBG("\r\nWRITEABLE???\r\n");
+ int res = (bool)( (m_pOutBufPos - m_outBufUsr) < BUF_LEN);
+ NVIC_EnableIRQ(USB_IRQn);
+ NVIC_EnableIRQ(US_TICKER_TIMER_IRQn);
+ return res;
+}
+
+void UsbSerial::onReadable()
+{
+ if(m_pInCbItem && m_pInCbMeth)
+ (m_pInCbItem->*m_pInCbMeth)();
+}
+
+void UsbSerial::onWriteable()
+{
+ if(m_pOutCbItem && m_pOutCbMeth)
+ (m_pOutCbItem->*m_pOutCbMeth)();
+}
+
+void UsbSerial::onEpInTransfer()
+{
+ int len = m_epIn.status();
+ DBG("RX transfer completed w len=%d\n",len);
+ startRx();
+ if(len > 0)
+ onReadable();
+}
+
+void UsbSerial::onEpOutTransfer()
+{
+ int len = m_epOut.status();
+ DBG("TX transfer completed w len=%d\n",len);
+ if(m_timeout)
+ m_txTimeout.detach();
+ startTx();
+ if(len > 0)
+ onWriteable();
+}
+
+void UsbSerial::startTx()
+{
+
+ DBG("Transfer>\n");
+
+ m_timeout = false;
+
+// m_txTimeout.detach();
+
+ if(!(m_pOutBufPos - m_outBufUsr))
+ {
+ DBG("?!?!?\n");
+ return;
+ }
+
+ if( m_epOut.status() == USBERR_PROCESSING )
+ {
+ //Wait & retry
+ //m_timeout = true;
+ //m_txTimeout.attach_us(this, &UsbSerial::startTx, FLUSH_TMOUT);
+ DBG("Ep is busy...\n");
+ return;
+ }
+
+ if( m_epOut.status() < 0 )
+ {
+ DBG("Tx trying again...\n");
+ m_epOut.transfer((volatile uint8_t*)m_outBufTrmt, m_outBufLen);
+ return;
+ }
+
+ m_outBufLen = m_pOutBufPos - m_outBufUsr;
+
+ //Swap buffers
+ volatile char* swapBuf = m_outBufUsr;
+ m_outBufUsr = m_outBufTrmt;
+ m_outBufTrmt = swapBuf;
+
+ m_epOut.transfer((volatile uint8_t*)m_outBufTrmt, m_outBufLen);
+
+ m_pOutBufPos = m_outBufUsr;
+
+}
+
+void UsbSerial::startRx()
+{
+ if( (m_pInBufPos - m_inBufUsr) < m_inBufLen )
+ {
+ //User buf is not empty, cannot swap now...
+ return;
+ }
+ int len = m_epIn.status();
+ if( len == USBERR_PROCESSING )
+ {
+ //Previous transmission not completed
+ return;
+ }
+ if( len < 0 )
+ {
+ DBG("Rx trying again...\n");
+ m_epIn.transfer((volatile uint8_t*)m_inBufTrmt, BUF_LEN); //Start another transmission
+ return;
+ }
+
+ m_inBufLen = len;
+
+ //Swap buffers
+ volatile char* swapBuf = m_inBufUsr;
+ m_inBufUsr = m_inBufTrmt;
+ m_inBufTrmt = swapBuf;
+ m_pInBufPos = m_inBufUsr;
+
+ DBG("Starting new transfer\n");
+ m_epIn.transfer((volatile uint8_t*)m_inBufTrmt, BUF_LEN); //Start another transmission
+
+}
+
+#ifdef MBED_RPC
+const struct rpc_method *UsbSerial::get_rpc_methods() {
+ static const rpc_method methods[] = {
+ { "readable", rpc_method_caller<int, UsbSerial, &UsbSerial::readable> },
+ { "writeable", rpc_method_caller<int, UsbSerial, &UsbSerial::writeable> },
+ RPC_METHOD_SUPER(Stream)
+ };
+ return methods;
+}
+
+struct rpc_class *UsbSerial::get_rpc_class() {
+ static const rpc_function funcs[] = {
+ /*{ "new", rpc_function_caller<const char*, UsbDevice*, int, int, const char*, Base::construct<UsbSerial,UsbDevice*,int,int,const char*> > },*/ //RPC is buggy
+ RPC_METHOD_END
+ };
+ static rpc_class c = { "UsbSerial", funcs, NULL };
+ return &c;
+}
+#endif
+
+} // namespace mbed
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/serial/usb/UsbSerial.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,189 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef USB_SERIAL_H
+#define USB_SERIAL_H
+
+//DG 2010
+//Essentially a clone of Serial if
+#include "Stream.h"
+#include "mbed.h"
+
+#include "drv/usb/UsbDevice.h"
+#include "drv/usb/UsbEndpoint.h"
+
+namespace mbed {
+
+class UsbSerial : public Stream {
+
+public:
+
+ UsbSerial(UsbDevice* pDevice, int epIn, int epOut, const char* name = NULL);
+ virtual ~UsbSerial();
+ //Apart from the ctor/dtor, exactly the same protos as Serial
+
+ void baud(int baudrate);
+
+ enum Parity {
+ None = 0,
+ Odd = 1,
+ Even = 2,
+ Forced1 = 3,
+ Forced0 = 4
+ };
+
+ enum IrqType {
+ RxIrq = 0
+ , TxIrq
+ };
+
+ void format(int bits, int parity, int stop);
+
+ class CDummy;
+ template <class T>
+ void attach(T* pCbItem, void (T::*pCbMeth)(), IrqType type = RxIrq)
+ {
+ if(type == RxIrq)
+ {
+ m_pInCbItem = (CDummy*) pCbItem;
+ m_pInCbMeth = (void (CDummy::*)()) pCbMeth;
+ }
+ else
+ {
+ m_pOutCbItem = (CDummy*) pCbItem;
+ m_pOutCbMeth = (void (CDummy::*)()) pCbMeth;
+ }
+ }
+
+#if 0 // Inhereted from Stream, for documentation only
+
+ /* Function: putc
+ * Write a character
+ *
+ * Variables:
+ * c - The character to write to the serial port
+ */
+ int putc(int c);
+
+ /* Function: getc
+ * Read a character
+ *
+ * Variables:
+ * returns - The character read from the serial port
+ */
+ int getc();
+
+ /* Function: printf
+ * Write a formated string
+ *
+ * Variables:
+ * format - A printf-style format string, followed by the
+ * variables to use in formating the string.
+ */
+ int printf(const char* format, ...);
+
+ /* Function: scanf
+ * Read a formated string
+ *
+ * Variables:
+ * format - A scanf-style format string,
+ * followed by the pointers to variables to store the results.
+ */
+ int scanf(const char* format, ...);
+
+#endif
+
+ /* Function: readable
+ * Determine if there is a character available to read
+ *
+ * Variables:
+ * returns - 1 if there is a character available to read, else 0
+ */
+ int readable();
+
+ /* Function: writeable
+ * Determine if there is space available to write a character
+ *
+ * Variables:
+ * returns - 1 if there is space to write a character, else 0
+ */
+ int writeable();
+
+#ifdef MBED_RPC
+ virtual const struct rpc_method *get_rpc_methods();
+ static struct rpc_class *get_rpc_class();
+#endif
+
+protected:
+
+ virtual int _getc();
+ virtual int _putc(int c);
+
+ void onReadable();
+ void onWriteable();
+
+ void onEpInTransfer();
+ void onEpOutTransfer();
+
+private:
+
+ UsbEndpoint m_epIn;
+ UsbEndpoint m_epOut;
+
+ CDummy* m_pInCbItem;
+ void (CDummy::*m_pInCbMeth)();
+
+ CDummy* m_pOutCbItem;
+ void (CDummy::*m_pOutCbMeth)();
+
+ void startTx();
+ void startRx();
+
+ Timeout m_txTimeout;
+ volatile bool m_timeout;
+
+ volatile char* m_inBufEven;
+ volatile char* m_inBufOdd;
+ volatile char* m_inBufUsr;
+ volatile char* m_inBufTrmt;
+
+ volatile char* m_outBufEven;
+ volatile char* m_outBufOdd;
+ volatile char* m_outBufUsr;
+ volatile char* m_outBufTrmt;
+
+ volatile int m_inBufLen;
+ volatile int m_outBufLen;
+
+ volatile char* m_pInBufPos;
+ volatile char* m_pOutBufPos;
+
+
+
+};
+
+}
+
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/umtsstick/UMTSStick.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,326 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "netCfg.h"
+#if NET_UMTS
+
+#include "UMTSStick.h"
+
+#define __DEBUG
+#include "dbg/dbg.h"
+
+UMTSStick::UMTSStick() : m_host(), m_pDev(NULL)
+{
+
+}
+
+UMTSStick::~UMTSStick()
+{
+
+}
+
+
+UMTSStickErr UMTSStick::getSerial(UsbSerial** ppUsbSerial)
+{
+ m_host.init();
+
+ UMTSStickErr rc;
+
+ rc = waitForDevice();
+ if(rc)
+ return rc;
+
+ //Device is now enumerated, read table
+
+ uint16_t vid = m_pDev->getVid();
+ uint16_t pid = m_pDev->getPid();
+
+ DBG("Configuration set: vid:%04x pid:%04x\n", vid, pid);
+
+ bool handled = false;
+ bool cdfs = false;
+ const UMTSSwitchingInfo* pInfo;
+ for(int i = 0; i < UMTS_SWITCHING_COUNT; i++)
+ {
+ pInfo = &UMTSwitchingTable[i];
+ if( !checkDeviceState(pInfo, &cdfs) )
+ {
+ handled = true;
+ break;
+ }
+
+ } //for(int i = 0; i < UMTS_SWITCHING_COUNT; i++)
+
+ if(!handled)
+ {
+ DBG("Don't know this device!\n");
+ return UMTSERR_NOTIMPLEMENTED;
+ }
+
+ //Check if the device is in CDFS mode, in this case switch
+ if(cdfs)
+ {
+ DBG("Switching the device by sending a magic packet\n");
+
+ rc = switchMode(pInfo);
+ if(rc)
+ return rc;
+
+ DBG("Now wait for device to reconnect\n");
+
+ m_host.releaseDevice(m_pDev);
+
+ //Wait for device to reconnect
+ wait(3);
+ rc = waitForDevice();
+ if(rc)
+ return rc;
+ }
+
+ rc = findSerial(ppUsbSerial);
+ if(rc)
+ return rc;
+
+ return UMTSERR_OK;
+}
+
+UMTSStickErr UMTSStick::waitForDevice()
+{
+ bool ready = false;
+ while(!ready)
+ {
+ while(!m_host.devicesCount())
+ {}
+ wait(1);
+ if(m_host.devicesCount())
+ ready = true;
+ }
+
+ wait(2); //Wait for device to be initialized
+
+ if(!m_host.devicesCount())
+ return UMTSERR_DISCONNECTED;
+
+ m_pDev = m_host.getDevice(0);
+
+ while(!m_pDev->enumerated())
+ {
+ m_host.poll();
+ if(!m_host.devicesCount())
+ return UMTSERR_DISCONNECTED;
+ }
+
+ return UMTSERR_OK;
+}
+
+UMTSStickErr UMTSStick::checkDeviceState(const UMTSSwitchingInfo* pInfo, bool* pCdfs)
+{
+ uint16_t vid = m_pDev->getVid();
+ uint16_t pid = m_pDev->getPid();
+ bool handled = false;
+ if( (vid == pInfo->cdfsVid) && (pid == pInfo->cdfsPid) )
+ {
+ DBG("Match on dongles list\n");
+ if( !pInfo->targetClass ) //No specific interface to check, vid/pid couple is specific to CDFS mode
+ {
+ DBG("Found device in CDFS mode\n");
+ handled = true;
+ *pCdfs = true;
+ }
+ else //if( !pInfo->targetClass )
+ {
+ //Has to check if there is an interface of class targetClass
+ byte* desc = NULL;
+ int c = 0;
+
+ while( !m_pDev->getInterfaceDescriptor(1, c++, &desc) )
+ {
+ if( desc[5] == pInfo->targetClass )
+ {
+ DBG("Found device in Serial mode\n");
+ handled = true;
+ *pCdfs = false;
+ break;
+ }
+ }
+
+ if(!handled)
+ {
+ //All interfaces were tried, so we are in CDFS mode
+ DBG("Found device in CDFS mode\n");
+ handled = true;
+ *pCdfs = true;
+ }
+ } //if( !pInfo->targetClass )
+ } //if( (vid == pInfo->cdfsVid) && (pid == pInfo->cdfsPid) )
+ else
+ {
+ //Try every vid/pid couple of the serial list
+ for( int i = 0; i < 16 ; i++)
+ {
+ if(!pInfo->serialPidList[i])
+ break;
+ if( (pInfo->serialVid == vid) && (pInfo->serialPidList[i] == pid) )
+ {
+ DBG("Found device in Serial mode\n");
+ handled = true;
+ *pCdfs = false;
+ break;
+ }
+ }
+ } //if( (vid == pInfo->cdfsVid) && (pid == pInfo->cdfsPid) )
+
+ if(!handled)
+ return UMTSERR_NOTFOUND;
+
+ return UMTSERR_OK;
+}
+
+UMTSStickErr UMTSStick::switchMode(const UMTSSwitchingInfo* pInfo)
+{
+ if(!pInfo->huaweiPacket) //Send SCSI packet on first bulk ep
+ {
+ //Find first bulk ep
+ byte* desc = NULL;
+ int c = 0;
+
+ UsbEndpoint *pEpOut = NULL;
+
+ while( !m_pDev->getInterfaceDescriptor(1, c++, &desc) )
+ {
+ byte* p = desc;
+ int epNum = 0;
+ p = p + p[0]; //Move to next descriptor (which should be an ep descriptor)
+ while (epNum < desc[4]) //Eps count in this if
+ {
+ if (p[1] != USB_DESCRIPTOR_TYPE_ENDPOINT)
+ break;
+
+ if( (p[3] == 0x02) && !(p[2] & 0x80) ) //Bulk endpoint, out
+ {
+ DBG("Found bulk ep %02x\n", p[2]);
+ pEpOut = new UsbEndpoint( m_pDev, p[2], false, USB_BULK, *((uint16_t*)&p[4]) );
+ break;
+ }
+
+ p = p + p[0]; //Move to next ep desc
+ epNum++;
+ }
+ if(pEpOut)
+ break;
+ }
+
+ if(!pEpOut)
+ return UMTSERR_NOTFOUND;
+
+ //Send SCSI packet
+
+ DBG("Sending SCSI Packet to switch\n");
+ byte ramCdfsBuf[31];
+ memcpy(ramCdfsBuf, pInfo->cdfsPacket, 31);
+ pEpOut->transfer((volatile byte*)ramCdfsBuf, 31);
+ while(pEpOut->status() == USBERR_PROCESSING);
+ int ret = pEpOut->status();
+ if((ret < 0) && (ret !=USBERR_DISCONNECTED)) //Packet was not transfered
+ {
+ DBG("Usb error %d\n", ret);
+ delete pEpOut;
+ return UMTSERR_USBERR;
+ }
+
+ delete pEpOut;
+ }
+ else
+ {
+ UsbErr usbErr;
+ //Send the Huawei-specific control packet
+ usbErr = m_pDev->controlSend(0, 0x03, 1, 0, NULL, 0);
+ if(usbErr && (usbErr != USBERR_DISCONNECTED))
+ return UMTSERR_USBERR;
+ }
+
+ DBG("The stick should be switching in serial mode now\n");
+
+ return UMTSERR_OK;
+}
+
+UMTSStickErr UMTSStick::findSerial(UsbSerial** ppUsbSerial)
+{
+ byte* desc = NULL;
+ int c = 0;
+
+ int epOut = 0;
+ int epIn = 0;
+
+ while( !m_pDev->getInterfaceDescriptor(1, c++, &desc) )
+ {
+ byte* p = desc;
+ int epNum = 0;
+
+ DBG("Interface of type %02x\n", desc[5]);
+
+ if(desc[5] != 0xFF) //Not a serial-like if
+ continue;
+
+ p = p + p[0]; //Move to next descriptor (which should be an ep descriptor)
+ while (epNum < desc[4]) //Eps count in this if
+ {
+ if (p[1] == USB_DESCRIPTOR_TYPE_ENDPOINT)
+ {
+ if( (p[3] == 0x02) && !(p[2] & 0x80) && !epOut ) //Bulk endpoint, out
+ {
+ DBG("Found bulk out ep %02x of payload size %04x\n", p[2], *((uint16_t*)&p[4]));
+ epOut = p[2] & 0x7F;
+ }
+
+ if( (p[3] == 0x02) && (p[2] & 0x80) && !epIn ) //Bulk endpoint, in
+ {
+ DBG("Found bulk in ep %02x of payload size %04x\n", p[2], *((uint16_t*)&p[4]));
+ epIn = p[2] & 0x7F;
+ }
+
+ if(epOut && epIn)
+ break;
+ }
+
+ p = p + p[0]; //Move to next ep desc
+ epNum++;
+ }
+
+ if(epOut && epIn)
+ break;
+ }
+
+ if(!epOut || !epIn)
+ return UMTSERR_NOTFOUND;
+
+ DBG("Endpoints found, create serial object\n");
+
+ *ppUsbSerial = new UsbSerial(m_pDev, epIn, epOut);
+
+ DBG("UsbSerial object created\n");
+
+ return UMTSERR_OK;
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/umtsstick/UMTSStick.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,87 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+/** \file
+UMTS Stick driver header file
+*/
+
+#ifndef UMTS_STICK_H
+#define UMTS_STICK_H
+
+#include "mbed.h"
+
+#include "drv/usb/UsbHostMgr.h"
+#include "drv/usb/UsbDevice.h"
+#include "drv/usb/UsbEndpoint.h"
+
+#include "drv/serial/usb/UsbSerial.h"
+
+#define UMTS_SWITCHING_COUNT 2
+
+typedef unsigned char byte;
+
+struct UMTSSwitchingInfo
+{
+ uint16_t cdfsVid;
+ uint16_t cdfsPid;
+ uint16_t serialVid;
+ uint16_t serialPidList[16];
+ byte targetClass;
+ bool huaweiPacket;
+ byte cdfsPacket[31];
+};
+
+extern const UMTSSwitchingInfo UMTSwitchingTable[UMTS_SWITCHING_COUNT];
+
+///UMTS Stick error codes
+enum UMTSStickErr
+{
+ __UMTSERR_MIN = -0xFFFF,
+ UMTSERR_NOTFOUND, ///<Stick was not found
+ UMTSERR_NOTIMPLEMENTED, ///<This model is not implemented
+ UMTSERR_USBERR, ///<USB Error
+ UMTSERR_DISCONNECTED, ///<Stick disconnected
+ UMTSERR_OK = 0 ///<Success
+};
+
+class UMTSStick
+{
+public:
+ UMTSStick();
+ ~UMTSStick();
+
+ UMTSStickErr getSerial(UsbSerial** ppUsbSerial);
+
+private:
+ UMTSStickErr waitForDevice();
+ UMTSStickErr checkDeviceState(const UMTSSwitchingInfo* pInfo, bool* pCdfs);
+
+ UMTSStickErr switchMode(const UMTSSwitchingInfo* pInfo);
+ UMTSStickErr findSerial(UsbSerial** ppUsbSerial);
+
+
+ UsbHostMgr m_host;
+ UsbDevice* m_pDev;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/umtsstick/UMTSStickData.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,24 @@
+#include "UMTSStick.h"
+
+const UMTSSwitchingInfo UMTSwitchingTable[UMTS_SWITCHING_COUNT] = {
+
+/*
+struct UMTSSwitchingInfo
+{
+ uint16_t cdfsVid;
+ uint16_t cdfsPid;
+ uint16_t serialVid;
+ uint16_t serialPidList[16];
+ byte targetClass;
+ bool huaweiPacket;
+ byte cdfsPacket[31];
+};
+*/
+
+//Huawei E220, E230, E270, E870
+{ 0x12d1, 0x1003, 0, {0}, 0xFF, true, {0} },
+
+//Huawei E1550, E270+
+{ 0x12d1, 0x1446, 0x12d1, {0x1001, 0x1406, 0x140c, 0x14ac/*, 0x1003*/}, 0, false, { 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0, 0, 0, 0, 0, 0, 0, 0x11, 0x06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/usb/UsbDevice.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,291 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "UsbDevice.h"
+
+#include "netCfg.h"
+#if NET_USB
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+UsbDevice::UsbDevice( UsbHostMgr* pMgr, int hub, int port, int addr ) : m_pControlEp(NULL), /*m_controlEp( this, 0x00, false, USB_CONTROL, 8 ),*/
+m_pMgr(pMgr), m_connected(false), m_enumerated(false), m_hub(hub), m_port(port), m_addr(addr), m_refs(0),
+m_vid(0), m_pid(0)
+{
+
+}
+
+UsbDevice::~UsbDevice()
+{
+ if(m_pControlEp)
+ delete m_pControlEp;
+}
+
+UsbErr UsbDevice::enumerate()
+{
+ // USB_INT32S rc;
+
+ UsbErr rc;
+
+ DBG("Starting enumeration (m_pMgr = %p)\n", m_pMgr);
+
+#if 1
+ m_pMgr->resetPort(m_hub, m_port);
+#else
+ wait_ms(100); /* USB 2.0 spec says atleast 50ms delay beore port reset */
+ LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
+ while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS)
+ __WFI(); // Wait for port reset to complete...
+ LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
+ wait_ms(200); /* Wait for 100 MS after port reset */
+#endif
+
+ DBG("Port reset\n");
+
+ wait_ms(200);
+
+ m_pControlEp = new UsbEndpoint( this, 0x00, false, USB_CONTROL, 8, 0 );
+
+ //EDCtrl->Control = 8 << 16;/* Put max pkt size = 8 */
+ /* Read first 8 bytes of device desc */
+ rc = controlReceive(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, (USB_DESCRIPTOR_TYPE_DEVICE << 8)|(0), 0, m_controlDataBuf, 8);
+ if (rc)
+ {
+ DBG("RC=%d",rc);
+ return (rc);
+ }
+
+ DBG("Got descriptor, max ep size is %d\n", m_controlDataBuf[7]);
+
+ m_pControlEp->updateSize(m_controlDataBuf[7]); /* Get max pkt size of endpoint 0 */
+ rc = controlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, SET_ADDRESS, m_addr, 0, NULL, 0); /* Set the device address to m_addr */
+ if (rc)
+ {
+ // PRINT_Err(rc);
+ return (rc);
+ }
+ wait_ms(2);
+ //EDCtrl->Control = (EDCtrl->Control) | 1; /* Modify control pipe with address 1 */
+
+ //Update address
+ m_pControlEp->updateAddr(m_addr);
+ DBG("Ep addr is now %d", m_addr);
+ /**/
+
+ //rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_DEVICE, 0, TDBuffer, 17); //Read full device descriptor
+ rc = controlReceive(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, (USB_DESCRIPTOR_TYPE_DEVICE << 8)|(0), 0, m_controlDataBuf, 17);
+ if (rc)
+ {
+ //PRINT_Err(rc);
+ return (rc);
+ }
+ /*
+ rc = SerialCheckVidPid();
+ if (rc != OK) {
+ PRINT_Err(rc);
+ return (rc);
+ }
+ */
+ /**/
+
+ m_vid = *((uint16_t*)&m_controlDataBuf[8]);
+ m_pid = *((uint16_t*)&m_controlDataBuf[10]);
+
+ DBG("VID: %02x, PID: %02x\n", m_vid, m_pid);
+ /* Get the configuration descriptor */
+ //rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, TDBuffer, 9);
+ rc = controlReceive(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, (USB_DESCRIPTOR_TYPE_CONFIGURATION << 8)|(0), 0, m_controlDataBuf, 9);
+ if (rc)
+ {
+ //PRINT_Err(rc);
+ return (rc);
+ }
+ /* Get the first configuration data */
+ //rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, TDBuffer, *((uint16_t*)&TDBuffer[2]));
+ rc = controlReceive(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, (USB_DESCRIPTOR_TYPE_CONFIGURATION << 8)|(0), 0, m_controlDataBuf, *((uint16_t*)&m_controlDataBuf[2]));
+ if (rc)
+ {
+ //PRINT_Err(rc);
+ return (rc);
+ }
+
+ DBG("Desc len is %d\n", *((uint16_t*)&m_controlDataBuf[2]));
+
+ DBG("Set configuration\n");
+
+ //rc = USBH_SET_CONFIGURATION(1);/* Select device configuration 1 */
+ rc = controlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, SET_CONFIGURATION, 1, 0, NULL, 0);
+ if (rc)
+ {
+ // PRINT_Err(rc);
+ return rc;
+ }
+ wait_ms(100);/* Some devices may require this delay */
+
+ m_enumerated = true;
+ return USBERR_OK;
+}
+
+bool UsbDevice::connected()
+{
+ return m_connected;
+}
+
+bool UsbDevice::enumerated()
+{
+ return m_enumerated;
+}
+
+int UsbDevice::getPid()
+{
+ return m_pid;
+}
+
+int UsbDevice::getVid()
+{
+ return m_vid;
+}
+
+UsbErr UsbDevice::getConfigurationDescriptor(int config, uint8_t** pBuf)
+{
+ //For now olny one config
+ *pBuf = m_controlDataBuf;
+ return USBERR_OK;
+}
+
+UsbErr UsbDevice::getInterfaceDescriptor(int config, int item, uint8_t** pBuf)
+{
+ byte* desc_ptr = m_controlDataBuf;
+
+/* if (desc_ptr[1] != USB_DESCRIPTOR_TYPE_CONFIGURATION)
+ {
+ return USBERR_BADCONFIG;
+ }*/
+
+ if(item>=m_controlDataBuf[4])//Interfaces count
+ return USBERR_NOTFOUND;
+
+ desc_ptr += desc_ptr[0];
+
+ *pBuf = NULL;
+
+ while (desc_ptr < m_controlDataBuf + *((uint16_t*)&m_controlDataBuf[2]))
+ {
+
+ switch (desc_ptr[1]) {
+ case USB_DESCRIPTOR_TYPE_INTERFACE:
+ if(desc_ptr[2] == item)
+ {
+ *pBuf = desc_ptr;
+ return USBERR_OK;
+ }
+ desc_ptr += desc_ptr[0]; // Move to next descriptor start
+ break;
+ }
+
+ }
+
+ if(*pBuf == NULL)
+ return USBERR_NOTFOUND;
+
+ return USBERR_OK;
+}
+
+
+UsbErr UsbDevice::setConfiguration(int config)
+{
+ return USBERR_OK;
+}
+
+UsbErr UsbDevice::controlSend(byte requestType, byte request, word value, word index, const byte* buf, int len)
+{
+ UsbErr rc;
+ fillControlBuf(requestType, request, value, index, len);
+ m_pControlEp->setNextToken(TD_SETUP);
+ rc = m_pControlEp->transfer(m_controlBuf, 8);
+ while(m_pControlEp->status() == USBERR_PROCESSING);
+ rc = (UsbErr) MIN(0, m_pControlEp->status());
+ if(rc)
+ return rc;
+ if(len)
+ {
+ m_pControlEp->setNextToken(TD_OUT);
+ rc = m_pControlEp->transfer((byte*)buf, len);
+ while(m_pControlEp->status() == USBERR_PROCESSING);
+ rc = (UsbErr) MIN(0, m_pControlEp->status());
+ if(rc)
+ return rc;
+ }
+ m_pControlEp->setNextToken(TD_IN);
+ rc = m_pControlEp->transfer(NULL, 0);
+ while(m_pControlEp->status() == USBERR_PROCESSING);
+ rc = (UsbErr) MIN(0, m_pControlEp->status());
+ if(rc)
+ return rc;
+ return USBERR_OK;
+}
+
+UsbErr UsbDevice::controlReceive(byte requestType, byte request, word value, word index, const byte* buf, int len)
+{
+ UsbErr rc;
+ fillControlBuf(requestType, request, value, index, len);
+ m_pControlEp->setNextToken(TD_SETUP);
+ rc = m_pControlEp->transfer(m_controlBuf, 8);
+ while(m_pControlEp->status() == USBERR_PROCESSING);
+ rc = (UsbErr) MIN(0, m_pControlEp->status());
+ if(rc)
+ return rc;
+ if(len)
+ {
+ m_pControlEp->setNextToken(TD_IN);
+ rc = m_pControlEp->transfer( (byte*) buf, len);
+ while(m_pControlEp->status() == USBERR_PROCESSING);
+ rc = (UsbErr) MIN(0, m_pControlEp->status());
+ if(rc)
+ return rc;
+ }
+ m_pControlEp->setNextToken(TD_OUT);
+ rc = m_pControlEp->transfer(NULL, 0);
+ while(m_pControlEp->status() == USBERR_PROCESSING);
+ rc = (UsbErr) MIN(0, m_pControlEp->status());
+ if(rc)
+ return rc;
+ return USBERR_OK;
+}
+
+void UsbDevice::fillControlBuf(byte requestType, byte request, word value, word index, int len)
+{
+#ifdef __BIG_ENDIAN
+ #error "Must implement BE to LE conv here"
+#endif
+ m_controlBuf[0] = requestType;
+ m_controlBuf[1] = request;
+ //We are in LE so it's fine
+ *((word*)&m_controlBuf[2]) = value;
+ *((word*)&m_controlBuf[4]) = index;
+ *((word*)&m_controlBuf[6]) = (word) len;
+}
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/usb/UsbDevice.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,87 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef USB_DEVICE_H
+#define USB_DEVICE_H
+
+#include "mbed.h"
+#include "UsbInc.h"
+#include "UsbEndpoint.h"
+#include "UsbHostMgr.h"
+
+class UsbHostMgr;
+class UsbEndpoint;
+
+class UsbDevice
+{
+protected:
+ UsbDevice( UsbHostMgr* pMgr, int hub, int port, int addr );
+ ~UsbDevice();
+
+ UsbErr enumerate();
+
+public:
+ bool connected();
+ bool enumerated();
+
+ int getPid();
+ int getVid();
+
+ UsbErr getConfigurationDescriptor(int config, uint8_t** pBuf);
+
+ UsbErr getInterfaceDescriptor(int config, int item, uint8_t** pBuf);
+
+ UsbErr setConfiguration(int config);
+
+
+ UsbErr controlSend(byte requestType, byte request, word value, word index, const byte* buf, int len);
+ UsbErr controlReceive(byte requestType, byte request, word value, word index, const byte* buf, int len);
+
+protected:
+ void fillControlBuf(byte requestType, byte request, word value, word index, int len);
+private:
+ friend class UsbEndpoint;
+ friend class UsbHostMgr;
+
+ UsbEndpoint* m_pControlEp;
+
+ UsbHostMgr* m_pMgr;
+
+ bool m_connected;
+ bool m_enumerated;
+
+ int m_hub;
+ int m_port;
+ int m_addr;
+
+ int m_refs;
+
+ uint16_t m_vid;
+ uint16_t m_pid;
+
+ byte m_controlBuf[8];//8
+ byte m_controlDataBuf[/*128*/256];
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/usb/UsbEndpoint.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,394 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "UsbEndpoint.h"
+
+#include "UsbDevice.h"
+
+#include "usb_mem.h"
+
+#include "netCfg.h"
+#if NET_USB
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+UsbEndpoint::UsbEndpoint( UsbDevice* pDevice, uint8_t ep, bool dir, UsbEndpointType type, uint16_t size, int addr /*= -1*/ ) : m_pDevice(pDevice), m_result(true), m_status((int)USBERR_OK), m_len(0), m_pBufStartPtr(NULL),
+m_pCbItem(NULL), m_pCbMeth(NULL), m_pNextEp(NULL)
+{
+ //Insert into Eps list
+ //FIXME: Assert that no USB interrupt is triggered meanwhile
+ if(m_pHeadEp)
+ {
+ m_pNextEp = m_pHeadEp;
+ m_pHeadEp = this;
+ }
+ else
+ {
+ m_pNextEp = NULL;
+ m_pHeadEp = this;
+ }
+
+ m_pEd = (volatile HCED*)usb_get_ed();
+
+ m_pTdHead = (volatile HCTD*)usb_get_td();
+ m_pTdTail = (volatile HCTD*)usb_get_td();
+
+ //printf("\r\n--m_pEd = %p--\r\n", m_pEd);
+ DBG("m_pEd = %p\n", m_pEd);
+ DBG("m_pTdHead = %p\n", m_pTdHead);
+ DBG("m_pTdTail = %p\n", m_pTdTail);
+
+ //Init Ed & Td
+ //printf("\r\n--Ep Init--\r\n");
+ memset((void*)m_pEd, 0, sizeof(HCED));
+ //printf("\r\n--Td Init--\r\n");
+
+ memset((void*)m_pTdHead, 0, sizeof(HCTD));
+ memset((void*)m_pTdTail, 0, sizeof(HCTD));
+
+ if(addr == -1)
+ addr = pDevice->m_addr;
+
+ //Setup Ed
+ //printf("\r\n--Ep Setup--\r\n");
+ m_pEd->Control = addr | /* USB address */
+ ((ep & 0x7F) << 7) | /* Endpoint address */
+ (type!=USB_CONTROL?((dir?2:1) << 11):0) | /* direction : Out = 1, 2 = In */
+ (size << 16); /* MaxPkt Size */
+
+ m_dir = dir;
+ m_setup = false;
+ m_type = type;
+
+ m_pEd->TailTd = m_pEd->HeadTd = (uint32_t) m_pTdTail; //Empty TD list
+
+ DBG("Before link\n");
+
+ //printf("\r\n--Ep Reg--\r\n");
+ //Append Ed to Ed list
+ volatile HCED* prevEd;
+ switch( m_type )
+ {
+ case USB_CONTROL:
+ prevEd = (volatile HCED*) LPC_USB->HcControlHeadED;
+ break;
+ case USB_BULK:
+ default:
+ prevEd = (volatile HCED*) LPC_USB->HcBulkHeadED;
+ break;
+ }
+
+ DBG("prevEd is %p\n", prevEd);
+
+ if(prevEd)
+ {
+ DBG("prevEd set\n")
+
+ while(prevEd->Next)
+ {
+ DBG("prevEd->Next = %08x\n", prevEd->Next);
+ prevEd = (volatile HCED*) prevEd->Next;
+ }
+ prevEd->Next = (uint32_t) m_pEd;
+
+ }
+ else
+ {
+ switch( m_type )
+ {
+ case USB_CONTROL:
+ LPC_USB->HcControlHeadED = (uint32_t) m_pEd;
+ break;
+ case USB_BULK:
+ default:
+ LPC_USB->HcBulkHeadED = (uint32_t) m_pEd;
+ break;
+ }
+ }
+
+ DBG("Ep init\n");
+}
+
+UsbEndpoint::~UsbEndpoint()
+{
+ //Remove from Eps list
+ //FIXME: Assert that no USB interrupt is triggered meanwhile
+ if(m_pHeadEp != this)
+ {
+ UsbEndpoint* prevEp = m_pHeadEp;
+ while(prevEp->m_pNextEp != this)
+ prevEp = prevEp->m_pNextEp;
+ prevEp->m_pNextEp = m_pNextEp;
+ }
+ else
+ {
+ m_pHeadEp = m_pNextEp;
+ }
+
+ m_pEd->Control |= ED_SKIP; //Skip this Ep in queue
+
+ //Remove from queue
+ volatile HCED* prevEd;
+ switch( m_type )
+ {
+ case USB_CONTROL:
+ prevEd = (volatile HCED*) LPC_USB->HcControlHeadED;
+ break;
+ case USB_BULK:
+ default:
+ prevEd = (volatile HCED*) LPC_USB->HcBulkHeadED;
+ break;
+ }
+ if( m_pEd == prevEd )
+ {
+ switch( m_type )
+ {
+ case USB_CONTROL:
+ LPC_USB->HcControlHeadED = m_pEd->Next;
+ break;
+ case USB_BULK:
+ default:
+ LPC_USB->HcBulkHeadED = m_pEd->Next;
+ break;
+ }
+ LPC_USB->HcBulkHeadED = m_pEd->Next;
+ }
+ else
+ {
+ while( prevEd->Next != (uint32_t) m_pEd )
+ {
+ prevEd = (volatile HCED*) prevEd->Next;
+ }
+ prevEd->Next = m_pEd->Next;
+ }
+
+ //
+ usb_free_ed((volatile byte*)m_pEd);
+
+ usb_free_td((volatile byte*)m_pTdHead);
+ usb_free_td((volatile byte*)m_pTdTail);
+}
+
+void UsbEndpoint::setNextToken(uint32_t token) //Only for control Eps
+{
+ switch(token)
+ {
+ case TD_SETUP:
+ m_dir = false;
+ m_setup = true;
+ break;
+ case TD_IN:
+ m_dir = true;
+ m_setup = false;
+ break;
+ case TD_OUT:
+ m_dir = false;
+ m_setup = false;
+ break;
+ }
+}
+
+UsbErr UsbEndpoint::transfer(volatile uint8_t* buf, uint32_t len)
+{
+ if(!m_result)
+ return USBERR_BUSY; //The previous trasnfer is not completed
+ //FIXME: We should be able to queue the next transfer, still needs to be implemented
+
+ if( !m_pDevice->connected() )
+ return USBERR_DISCONNECTED;
+
+ m_result = false;
+
+ volatile uint32_t token = (m_setup?TD_SETUP:(m_dir?TD_IN:TD_OUT));
+
+ volatile uint32_t td_toggle;
+ if (m_type == USB_CONTROL)
+ {
+ if (m_setup)
+ {
+ td_toggle = TD_TOGGLE_0;
+ }
+ else
+ {
+ td_toggle = TD_TOGGLE_1;
+ }
+ }
+ else
+ {
+ td_toggle = 0;
+ }
+
+ //Swap Tds
+ volatile HCTD* pTdSwap;
+ pTdSwap = m_pTdTail;
+ m_pTdTail = m_pTdHead;
+ m_pTdHead = pTdSwap;
+
+ m_pTdHead->Control = (TD_ROUNDING |
+ token |
+ TD_DELAY_INT(0) |//7
+ td_toggle |
+ TD_CC);
+
+ m_pTdTail->Control = 0;
+ m_pTdHead->CurrBufPtr = (uint32_t) buf;
+ m_pBufStartPtr = buf;
+ m_pTdTail->CurrBufPtr = 0;
+ m_pTdHead->Next = (uint32_t) m_pTdTail;
+ m_pTdTail->Next = 0;
+ m_pTdHead->BufEnd = (uint32_t)(buf + (len - 1));
+ m_pTdTail->BufEnd = 0;
+
+ m_pEd->HeadTd = (uint32_t)m_pTdHead | ((m_pEd->HeadTd) & 0x00000002); //Carry bit
+ m_pEd->TailTd = (uint32_t)m_pTdTail;
+
+ //DBG("m_pEd->HeadTd = %08x\n", m_pEd->HeadTd);
+
+ if(m_type == USB_CONTROL)
+ {
+ LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | OR_CMD_STATUS_CLF;
+ LPC_USB->HcControl = LPC_USB->HcControl | OR_CONTROL_CLE; //Enable control list
+ }
+ else //USB_BULK
+ {
+ LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | OR_CMD_STATUS_BLF;
+ LPC_USB->HcControl = LPC_USB->HcControl | OR_CONTROL_BLE; //Enable bulk list
+ }
+
+ //m_done = false;
+ m_len = len;
+
+ return USBERR_PROCESSING;
+
+}
+
+int UsbEndpoint::status()
+{
+ if( !m_pDevice->connected() )
+ {
+ if(!m_result)
+ onCompletion();
+ m_result = true;
+ return (int)USBERR_DISCONNECTED;
+ }
+ else if( !m_result )
+ {
+ return (int)USBERR_PROCESSING;
+ }
+ /*else if( m_done )
+ {
+ return (int)USBERR_OK;
+ }*/
+ else
+ {
+ return m_status;
+ }
+}
+
+void UsbEndpoint::updateAddr(int addr)
+{
+ DBG("m_pEd->Control = %08x\n", m_pEd->Control);
+ m_pEd->Control &= ~0x7F;
+ m_pEd->Control |= addr;
+ DBG("m_pEd->Control = %08x\n", m_pEd->Control);
+}
+
+void UsbEndpoint::updateSize(uint16_t size)
+{
+ DBG("m_pEd->Control = %08x\n", m_pEd->Control);
+ m_pEd->Control &= ~0x3FF0000;
+ m_pEd->Control |= (size << 16);
+ DBG("m_pEd->Control = %08x\n", m_pEd->Control);
+}
+
+#if 0 //For doc only
+template <class T>
+void UsbEndpoint::setOnCompletion( T* pCbItem, void (T::*pCbMeth)() )
+{
+ m_pCbItem = (CDummy*) pCbItem;
+ m_pCbMeth = (void (CDummy::*)()) pCbMeth;
+}
+#endif
+
+void UsbEndpoint::onCompletion()
+{
+ //DBG("Transfer completed\n");
+ if( m_pTdHead->Control >> 28 )
+ {
+ DBG("TD Failed with condition code %01x\n", m_pTdHead->Control >> 28 );
+ m_status = (int)USBERR_TDFAIL;
+ }
+ else if( m_pEd->HeadTd & 0x1 )
+ {
+ m_pEd->HeadTd = m_pEd->HeadTd & ~0x1;
+ DBG("\r\nHALTED!!\r\n");
+ m_status = (int)USBERR_HALTED;
+ }
+ else if( (m_pEd->HeadTd & ~0xF) == (uint32_t) m_pTdTail )
+ {
+ //Done
+ int len;
+ //DBG("m_pTdHead->CurrBufPtr = %08x, m_pBufStartPtr=%08x\n", m_pTdHead->CurrBufPtr, (uint32_t) m_pBufStartPtr);
+ if(m_pTdHead->CurrBufPtr)
+ len = m_pTdHead->CurrBufPtr - (uint32_t) m_pBufStartPtr;
+ else
+ len = m_len;
+ /*if(len == 0) //Packet transfered completely
+ len = m_len;*/
+ //m_done = true;
+ DBG("Transfered %d bytes\n", len);
+ m_status = len;
+ }
+ else
+ {
+ DBG("Unknown error...\n");
+ m_status = (int)USBERR_ERROR;
+ }
+ m_result = true;
+ if(m_pCbItem && m_pCbMeth)
+ (m_pCbItem->*m_pCbMeth)();
+}
+
+void UsbEndpoint::sOnCompletion(uint32_t pTd)
+{
+ if(!m_pHeadEp)
+ return;
+ do
+ {
+ //DBG("sOnCompletion (pTd = %08x)\n", pTd);
+ UsbEndpoint* pEp = m_pHeadEp;
+ do
+ {
+ if((uint32_t)pEp->m_pTdHead == pTd)
+ {
+ pEp->onCompletion();
+ break;
+ }
+ } while(pEp = pEp->m_pNextEp);
+ } while( pTd = (uint32_t)( ((HCTD*)pTd)->Next ) ); //Go around the Done queue
+}
+
+UsbEndpoint* UsbEndpoint::m_pHeadEp = NULL;
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/usb/UsbEndpoint.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,103 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef USB_ENDPOINT_H
+#define USB_ENDPOINT_H
+
+#include "mbed.h"
+#include "UsbInc.h"
+
+class UsbDevice;
+
+enum UsbEndpointType
+{
+ USB_CONTROL,
+ USB_BULK
+// USB_INT,
+// USB_ISO
+};
+
+class UsbEndpoint
+{
+public:
+ UsbEndpoint( UsbDevice* pDevice, uint8_t ep, bool dir, UsbEndpointType type, uint16_t size, int addr = -1 );
+ ~UsbEndpoint();
+
+ void setNextToken(uint32_t token); //Only for control Eps
+
+ UsbErr transfer(volatile uint8_t* buf, uint32_t len);
+
+ int status(); //return UsbErr or transfered len
+
+ void updateAddr(int addr);
+ void updateSize(uint16_t size);
+
+ //void setOnCompletion( void(*pCb)completed() );
+ class CDummy;
+ template <class T>
+ void setOnCompletion( T* pCbItem, void (T::*pCbMeth)() )
+ {
+ m_pCbItem = (CDummy*) pCbItem;
+ m_pCbMeth = (void (CDummy::*)()) pCbMeth;
+ }
+
+//static void completed(){}
+
+protected:
+ void onCompletion();
+public:
+ static void sOnCompletion(uint32_t pTd);
+
+private:
+ friend class UsbDevice;
+
+ UsbDevice* m_pDevice;
+
+ bool m_dir;
+ bool m_setup;
+ UsbEndpointType m_type;
+
+ //bool m_done;
+ volatile bool m_result;
+ volatile int m_status;
+
+ volatile uint32_t m_len;
+
+ volatile uint8_t* m_pBufStartPtr;
+
+ volatile HCED* m_pEd; //Ep descriptor
+ volatile HCTD* m_pTdHead; //Head trf descriptor
+ volatile HCTD* m_pTdTail; //Tail trf descriptor
+
+ //static volatile HCED* m_pNextEd;
+
+ CDummy* m_pCbItem;
+ void (CDummy::*m_pCbMeth)();
+
+
+ static UsbEndpoint* m_pHeadEp;
+ UsbEndpoint* m_pNextEp;
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/usb/UsbHostMgr.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,367 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "UsbHostMgr.h"
+
+#include "usb_mem.h"
+
+#include "string.h" //For memcpy, memmove, memset
+
+#include "netCfg.h"
+#if NET_USB
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+// bits of the USB/OTG clock control register
+#define HOST_CLK_EN (1<<0)
+#define DEV_CLK_EN (1<<1)
+#define PORTSEL_CLK_EN (1<<3)
+#define AHB_CLK_EN (1<<4)
+
+// bits of the USB/OTG clock status register
+#define HOST_CLK_ON (1<<0)
+#define DEV_CLK_ON (1<<1)
+#define PORTSEL_CLK_ON (1<<3)
+#define AHB_CLK_ON (1<<4)
+
+// we need host clock, OTG/portsel clock and AHB clock
+#define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
+
+static UsbHostMgr* pMgr = NULL;
+
+extern "C" void sUsbIrqhandler(void) __irq
+{
+ DBG("\n+Int\n");
+ if(pMgr)
+ pMgr->UsbIrqhandler();
+ DBG("\n-Int\n");
+ return;
+}
+
+UsbHostMgr::UsbHostMgr() : m_lpDevices()
+{
+ /*if(!pMgr)*/ //Assume singleton
+ pMgr = this;
+ usb_mem_init();
+ memset(m_lpDevices, NULL, sizeof(UsbDevice*) * USB_HOSTMGR_MAX_DEVS);
+ m_pHcca = (HCCA*) usb_get_hcca();
+ memset((void*)m_pHcca, 0, 0x100);
+ DBG("Host manager at %p\n", this);
+}
+
+UsbHostMgr::~UsbHostMgr()
+{
+ if(pMgr == this)
+ pMgr = NULL;
+}
+
+UsbErr UsbHostMgr::init() //Initialize host
+{
+ NVIC_DisableIRQ(USB_IRQn); /* Disable the USB interrupt source */
+
+ LPC_SC->PCONP &= ~(1UL<<31); //Cut power
+ wait(1);
+
+
+ // turn on power for USB
+ LPC_SC->PCONP |= (1UL<<31);
+ // Enable USB host clock, port selection and AHB clock
+ LPC_USB->USBClkCtrl |= CLOCK_MASK;
+ // Wait for clocks to become available
+ while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK)
+ ;
+
+ // it seems the bits[0:1] mean the following
+ // 0: U1=device, U2=host
+ // 1: U1=host, U2=host
+ // 2: reserved
+ // 3: U1=host, U2=device
+ // NB: this register is only available if OTG clock (aka "port select") is enabled!!
+ // since we don't care about port 2, set just bit 0 to 1 (U1=host)
+ LPC_USB->OTGStCtrl |= 1;
+
+ // now that we've configured the ports, we can turn off the portsel clock
+ LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
+
+ // power pins are not connected on mbed, so we can skip them
+ /* P1[18] = USB_UP_LED, 01 */
+ /* P1[19] = /USB_PPWR, 10 */
+ /* P1[22] = USB_PWRD, 10 */
+ /* P1[27] = /USB_OVRCR, 10 */
+ /*LPC_PINCON->PINSEL3 &= ~((3<<4) | (3<<6) | (3<<12) | (3<<22));
+ LPC_PINCON->PINSEL3 |= ((1<<4)|(2<<6) | (2<<12) | (2<<22)); // 0x00802080
+ */
+
+ // configure USB D+/D- pins
+ /* P0[29] = USB_D+, 01 */
+ /* P0[30] = USB_D-, 01 */
+ LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));
+ LPC_PINCON->PINSEL1 |= ((1<<26)|(1<<28)); // 0x14000000
+
+ DBG("Initializing Host Stack\n");
+
+ wait_ms(100); /* Wait 50 ms before apply reset */
+ LPC_USB->HcControl = 0; /* HARDWARE RESET */
+ LPC_USB->HcControlHeadED = 0; /* Initialize Control list head to Zero */
+ LPC_USB->HcBulkHeadED = 0; /* Initialize Bulk list head to Zero */
+
+ /* SOFTWARE RESET */
+ LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
+ LPC_USB->HcFmInterval = DEFAULT_FMINTERVAL; /* Write Fm Interval and Largest Data Packet Counter */
+
+ /* Put HC in operational state */
+ LPC_USB->HcControl = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
+ LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC; /* Set Global Power */
+
+ LPC_USB->HcHCCA = (uint32_t)(m_pHcca);
+ LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; /* Clear Interrrupt Status */
+
+
+ LPC_USB->HcInterruptEnable = OR_INTR_ENABLE_MIE |
+ OR_INTR_ENABLE_WDH |
+ OR_INTR_ENABLE_RHSC;
+
+ NVIC_SetPriority(USB_IRQn, 0); /* highest priority */
+ /* Enable the USB Interrupt */
+ NVIC_SetVector(USB_IRQn, (uint32_t)(sUsbIrqhandler));
+ LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
+ LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+
+ /* Check for any connected devices */
+ if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) //Root device connected
+ {
+ //Device connected
+ wait(1);
+ DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
+ onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
+ }
+
+ DBG("Enabling IRQ\n");
+ NVIC_EnableIRQ(USB_IRQn);
+ DBG("End of host stack initialization\n");
+ return USBERR_OK;
+}
+
+void UsbHostMgr::poll() //Enumerate connected devices, etc
+{
+ /* Check for any connected devices */
+ if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) //Root device connected
+ {
+ //Device connected
+ wait(1);
+ DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
+ onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
+ }
+
+ for(int i = 0; i < devicesCount(); i++)
+ {
+ if( (m_lpDevices[i]->m_connected)
+ && !(m_lpDevices[i]->m_enumerated) )
+ {
+ m_lpDevices[i]->enumerate();
+ return;
+ }
+ }
+}
+
+int UsbHostMgr::devicesCount()
+{
+ int i;
+ for(i = 0; i < USB_HOSTMGR_MAX_DEVS; i++)
+ {
+ if (m_lpDevices[i] == NULL)
+ break;
+ }
+ return i;
+}
+
+UsbDevice* UsbHostMgr::getDevice(int item)
+{
+ UsbDevice* pDev = m_lpDevices[item];
+ if(!pDev)
+ return NULL;
+
+ pDev->m_refs++;
+ return pDev;
+}
+
+void UsbHostMgr::releaseDevice(UsbDevice* pDev)
+{
+ pDev->m_refs--;
+ if(pDev->m_refs > 0)
+ return;
+ //If refs count = 0, delete
+ //Find & remove from list
+ int i;
+ for(i = 0; i < USB_HOSTMGR_MAX_DEVS; i++)
+ {
+ if (m_lpDevices[i] == pDev)
+ break;
+ }
+ if(i!=USB_HOSTMGR_MAX_DEVS)
+ memmove(&m_lpDevices[i], &m_lpDevices[i+1], sizeof(UsbDevice*) * (USB_HOSTMGR_MAX_DEVS - (i + 1))); //Safer than memcpy because of overlapping mem
+ m_lpDevices[USB_HOSTMGR_MAX_DEVS - 1] = NULL;
+ delete pDev;
+}
+
+void UsbHostMgr::UsbIrqhandler()
+{
+ uint32_t int_status;
+ uint32_t ie_status;
+
+ int_status = LPC_USB->HcInterruptStatus; /* Read Interrupt Status */
+ ie_status = LPC_USB->HcInterruptEnable; /* Read Interrupt enable status */
+
+ if (!(int_status & ie_status))
+ {
+ return;
+ }
+ else
+ {
+ int_status = int_status & ie_status;
+ if (int_status & OR_INTR_STATUS_RHSC) /* Root hub status change interrupt */
+ {
+ DBG("LPC_USB->HcRhPortStatus1 = %08x\n", LPC_USB->HcRhPortStatus1);
+ if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC)
+ {
+ if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE)
+ {
+ /*
+ * When DRWE is on, Connect Status Change
+ * means a remote wakeup event.
+ */
+ //HOST_RhscIntr = 1;// JUST SOMETHING FOR A BREAKPOINT
+ }
+ else
+ {
+ /*
+ * When DRWE is off, Connect Status Change
+ * is NOT a remote wakeup event
+ */
+ if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) //Root device connected
+ {
+ //Device connected
+ DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
+ onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
+ }
+ else //Root device disconnected
+ {
+ //Device disconnected
+ DBG("Device disconnected\n");
+ onUsbDeviceDisconnected(0, 1);
+ }
+ //TODO: HUBS
+ }
+ LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
+ }
+ if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC)
+ {
+ LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+ }
+ }
+ if (int_status & OR_INTR_STATUS_WDH) /* Writeback Done Head interrupt */
+ {
+ //UsbEndpoint::sOnCompletion((LPC_USB->HccaDoneHead) & 0xFE);
+ if(m_pHcca->DoneHead)
+ {
+ UsbEndpoint::sOnCompletion(m_pHcca->DoneHead);
+ m_pHcca->DoneHead = 0;
+ LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
+ if(m_pHcca->DoneHead)
+ DBG("??????????????????????????????\n\n\n");
+ }
+ else
+ {
+ //Probably an error
+ int_status = LPC_USB->HcInterruptStatus;
+ DBG("HcInterruptStatus = %08x\n", int_status);
+ if (int_status & OR_INTR_STATUS_UE) //Unrecoverable error, disconnect devices and resume
+ {
+ onUsbDeviceDisconnected(0, 1);
+ LPC_USB->HcInterruptStatus = OR_INTR_STATUS_UE;
+ LPC_USB->HcCommandStatus = 0x01; //Host Controller Reset
+ }
+ }
+ }
+ LPC_USB->HcInterruptStatus = int_status; /* Clear interrupt status register */
+ }
+ return;
+}
+
+void UsbHostMgr::onUsbDeviceConnected(int hub, int port)
+{
+ int item = devicesCount();
+ if( item == USB_HOSTMGR_MAX_DEVS )
+ return; //List full...
+ //Find a free address (not optimized, but not really important)
+ int i;
+ int addr = 1;
+ for(i = 0; i < item; i++)
+ {
+ addr = MAX( addr, m_lpDevices[i]->m_addr + 1 );
+ }
+ m_lpDevices[item] = new UsbDevice( this, hub, port, addr );
+ m_lpDevices[item]->m_connected = true;
+}
+
+void UsbHostMgr::onUsbDeviceDisconnected(int hub, int port)
+{
+ for(int i = 0; i < devicesCount(); i++)
+ {
+ if( (m_lpDevices[i]->m_hub == hub)
+ && (m_lpDevices[i]->m_port == port) )
+ {
+ m_lpDevices[i]->m_connected = false;
+ if(!m_lpDevices[i]->m_enumerated)
+ {
+ delete m_lpDevices[i];
+ m_lpDevices[i] = NULL;
+ }
+ return;
+ }
+ }
+}
+
+void UsbHostMgr::resetPort(int hub, int port)
+{
+ DBG("Resetting hub %d, port %d\n", hub, port);
+ if(hub == 0) //Root hub
+ {
+ wait_ms(100); /* USB 2.0 spec says at least 50ms delay before port reset */
+ LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
+ DBG("Before loop\n");
+ while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS)
+ ;
+ LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
+ DBG("After loop\n");
+ wait_ms(200); /* Wait for 100 MS after port reset */
+ }
+ else
+ {
+ //TODO: Hubs
+ }
+ DBG("Port reset OK\n");
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/usb/UsbHostMgr.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,67 @@
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+//Assigns addresses to connected devices...
+
+#ifndef USB_HOSTMGR_H
+#define USB_HOSTMGR_H
+
+#include "mbed.h"
+#include "UsbInc.h"
+#include "UsbDevice.h"
+
+#define USB_HOSTMGR_MAX_DEVS 2 //2 devices max for now... will be more when hubs are supported
+
+class UsbDevice;
+
+class UsbHostMgr //[0-1] inst
+{
+public:
+ UsbHostMgr();
+ ~UsbHostMgr();
+
+ UsbErr init(); //Initialize host
+
+ void poll(); //Enumerate connected devices, etc
+
+ int devicesCount();
+
+ UsbDevice* getDevice(int item);
+ void releaseDevice(UsbDevice* pDev);
+
+
+ void UsbIrqhandler();
+
+protected:
+ void onUsbDeviceConnected(int hub, int port);
+ void onUsbDeviceDisconnected(int hub, int port);
+
+ friend class UsbDevice;
+ void resetPort(int hub, int port);
+
+private:
+/* __align(8)*/ volatile HCCA* m_pHcca;
+
+ UsbDevice* m_lpDevices[USB_HOSTMGR_MAX_DEVS];
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/usb/UsbInc.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,193 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef USB_INC_H
+#define USB_INC_H
+
+#include "mbed.h"
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+//typedef int32_t RC;
+
+typedef uint8_t byte;
+typedef uint16_t word;
+
+enum UsbErr
+{
+ __USBERR_MIN = -0xFFFF,
+ USBERR_DISCONNECTED,
+ USBERR_NOTFOUND,
+ USBERR_BADCONFIG,
+ USBERR_PROCESSING,
+ USBERR_HALTED, //Transfer on an ep is stalled
+ USBERR_BUSY,
+ USBERR_TDFAIL,
+ USBERR_ERROR,
+ USBERR_OK = 0
+};
+
+
+/* From NXP's USBHostLite stack's usbhost_lpc17xx.h */
+/* Only the types names have been changed to avoid unecessary typedefs */
+
+
+/*
+**************************************************************************************************************
+* NXP USB Host Stack
+*
+* (c) Copyright 2008, NXP SemiConductors
+* (c) Copyright 2008, OnChip Technologies LLC
+* All Rights Reserved
+*
+* www.nxp.com
+* www.onchiptech.com
+*
+* File : usbhost_lpc17xx.h
+* Programmer(s) : Ravikanth.P
+* Version :
+*
+**************************************************************************************************************
+*/
+
+
+
+/*
+**************************************************************************************************************
+* OHCI OPERATIONAL REGISTER FIELD DEFINITIONS
+**************************************************************************************************************
+*/
+
+ /* ------------------ HcControl Register --------------------- */
+#define OR_CONTROL_CLE 0x00000010
+#define OR_CONTROL_BLE 0x00000020
+#define OR_CONTROL_HCFS 0x000000C0
+#define OR_CONTROL_HC_OPER 0x00000080
+ /* ----------------- HcCommandStatus Register ----------------- */
+#define OR_CMD_STATUS_HCR 0x00000001
+#define OR_CMD_STATUS_CLF 0x00000002
+#define OR_CMD_STATUS_BLF 0x00000004
+ /* --------------- HcInterruptStatus Register ----------------- */
+#define OR_INTR_STATUS_WDH 0x00000002
+#define OR_INTR_STATUS_RHSC 0x00000040
+#define OR_INTR_STATUS_UE 0x00000010
+ /* --------------- HcInterruptEnable Register ----------------- */
+#define OR_INTR_ENABLE_WDH 0x00000002
+#define OR_INTR_ENABLE_RHSC 0x00000040
+#define OR_INTR_ENABLE_MIE 0x80000000
+ /* ---------------- HcRhDescriptorA Register ------------------ */
+#define OR_RH_STATUS_LPSC 0x00010000
+#define OR_RH_STATUS_DRWE 0x00008000
+ /* -------------- HcRhPortStatus[1:NDP] Register -------------- */
+#define OR_RH_PORT_CCS 0x00000001
+#define OR_RH_PORT_PRS 0x00000010
+#define OR_RH_PORT_CSC 0x00010000
+#define OR_RH_PORT_PRSC 0x00100000
+
+
+/*
+**************************************************************************************************************
+* FRAME INTERVAL
+**************************************************************************************************************
+*/
+
+#define FI 0x2EDF /* 12000 bits per frame (-1) */
+#define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI)
+
+/*
+**************************************************************************************************************
+* ENDPOINT DESCRIPTOR CONTROL FIELDS
+**************************************************************************************************************
+*/
+
+#define ED_SKIP (uint32_t) (0x00001000) /* Skip this ep in queue */
+
+/*
+**************************************************************************************************************
+* TRANSFER DESCRIPTOR CONTROL FIELDS
+**************************************************************************************************************
+*/
+
+#define TD_ROUNDING (uint32_t) (0x00040000) /* Buffer Rounding */
+#define TD_SETUP (uint32_t)(0) /* Direction of Setup Packet */
+#define TD_IN (uint32_t)(0x00100000) /* Direction In */
+#define TD_OUT (uint32_t)(0x00080000) /* Direction Out */
+#define TD_DELAY_INT(x) (uint32_t)((x) << 21) /* Delay Interrupt */
+#define TD_TOGGLE_0 (uint32_t)(0x02000000) /* Toggle 0 */
+#define TD_TOGGLE_1 (uint32_t)(0x03000000) /* Toggle 1 */
+#define TD_CC (uint32_t)(0xF0000000) /* Completion Code */
+
+/*
+**************************************************************************************************************
+* USB STANDARD REQUEST DEFINITIONS
+**************************************************************************************************************
+*/
+
+#define USB_DESCRIPTOR_TYPE_DEVICE 1
+#define USB_DESCRIPTOR_TYPE_CONFIGURATION 2
+#define USB_DESCRIPTOR_TYPE_INTERFACE 4
+#define USB_DESCRIPTOR_TYPE_ENDPOINT 5
+ /* ----------- Control RequestType Fields ----------- */
+#define USB_DEVICE_TO_HOST 0x80
+#define USB_HOST_TO_DEVICE 0x00
+#define USB_REQUEST_TYPE_CLASS 0x20
+#define USB_RECIPIENT_DEVICE 0x00
+#define USB_RECIPIENT_INTERFACE 0x01
+ /* -------------- USB Standard Requests -------------- */
+#define SET_ADDRESS 5
+#define GET_DESCRIPTOR 6
+#define SET_CONFIGURATION 9
+#define SET_INTERFACE 11
+
+/*
+**************************************************************************************************************
+* TYPE DEFINITIONS
+**************************************************************************************************************
+*/
+
+typedef struct hcEd { /* ----------- HostController EndPoint Descriptor ------------- */
+ volatile uint32_t Control; /* Endpoint descriptor control */
+ volatile uint32_t TailTd; /* Physical address of tail in Transfer descriptor list */
+ volatile uint32_t HeadTd; /* Physcial address of head in Transfer descriptor list */
+ volatile uint32_t Next; /* Physical address of next Endpoint descriptor */
+} HCED;
+
+typedef struct hcTd { /* ------------ HostController Transfer Descriptor ------------ */
+ volatile uint32_t Control; /* Transfer descriptor control */
+ volatile uint32_t CurrBufPtr; /* Physical address of current buffer pointer */
+ volatile uint32_t Next; /* Physical pointer to next Transfer Descriptor */
+ volatile uint32_t BufEnd; /* Physical address of end of buffer */
+} HCTD;
+
+typedef struct hcca { /* ----------- Host Controller Communication Area ------------ */
+ volatile uint32_t IntTable[32]; /* Interrupt Table */
+ volatile uint32_t FrameNumber; /* Frame Number */
+ volatile uint32_t DoneHead; /* Done Head */
+ volatile uint8_t Reserved[116]; /* Reserved for future use */
+ volatile uint8_t Unknown[4]; /* Unused */
+} HCCA;
+
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/usb/usb_mem.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,108 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "usb_mem.h"
+
+#include "string.h" //For memcpy, memmove, memset
+
+#include "netCfg.h"
+#if NET_USB
+
+#define EDS_COUNT 4
+#define TDS_COUNT 8
+
+#define HCCA_SIZE 0x100
+#define ED_SIZE 0x10
+#define TD_SIZE 0x10
+
+#define TOTAL_SIZE (HCCA_SIZE + (EDS_COUNT*ED_SIZE) + (TDS_COUNT*TD_SIZE))
+
+
+static volatile __align(256) byte usb_buf[TOTAL_SIZE] __attribute((section("AHBSRAM1"),aligned)); //256 bytes aligned!
+
+static volatile byte* usb_hcca; //256 bytes aligned!
+
+static volatile byte* usb_edBuf; //4 bytes aligned!
+static volatile byte* usb_tdBuf; //4 bytes aligned!
+
+static byte usb_edBufAlloc[EDS_COUNT] __attribute((section("AHBSRAM1"),aligned));
+static byte usb_tdBufAlloc[TDS_COUNT] __attribute((section("AHBSRAM1"),aligned));
+
+void usb_mem_init()
+{
+ usb_hcca = usb_buf;
+ usb_edBuf = usb_buf + HCCA_SIZE;
+ usb_tdBuf = usb_buf + HCCA_SIZE + (EDS_COUNT*ED_SIZE);
+ memset(usb_edBufAlloc, 0, EDS_COUNT);
+ memset(usb_tdBufAlloc, 0, TDS_COUNT);
+}
+
+volatile byte* usb_get_hcca()
+{
+ return usb_hcca;
+}
+
+volatile byte* usb_get_ed()
+{
+ int i;
+ for(i = 0; i < EDS_COUNT; i++)
+ {
+ if( !usb_edBufAlloc[i] )
+ {
+ usb_edBufAlloc[i] = 1;
+ return usb_edBuf + i*ED_SIZE;
+ }
+ }
+ return NULL; //Could not alloc ED
+}
+
+volatile byte* usb_get_td()
+{
+ int i;
+ for(i = 0; i < TDS_COUNT; i++)
+ {
+ if( !usb_tdBufAlloc[i] )
+ {
+ usb_tdBufAlloc[i] = 1;
+ return usb_tdBuf + i*TD_SIZE;
+ }
+ }
+ return NULL; //Could not alloc TD
+}
+
+void usb_free_ed(volatile byte* ed)
+{
+ int i;
+ i = (ed - usb_edBuf) / ED_SIZE;
+ usb_edBufAlloc[i] = 0;
+}
+
+void usb_free_td(volatile byte* td)
+{
+ int i;
+ i = (td - usb_tdBuf) / TD_SIZE;
+ usb_tdBufAlloc[i] = 0;
+}
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/drv/usb/usb_mem.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,48 @@
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef USB_MEM_H
+#define USB_MEM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned char byte;
+
+void usb_mem_init();
+
+volatile byte* usb_get_hcca();
+
+volatile byte* usb_get_ed();
+
+volatile byte* usb_get_td();
+
+void usb_free_ed(volatile byte* ed);
+
+void usb_free_td(volatile byte* td);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/eth/EthernetNetIf.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,192 @@
+#pragma diag_remark 1464
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "EthernetNetIf.h"
+
+#include "netif/etharp.h"
+#include "lwip/dhcp.h"
+#include "lwip/dns.h"
+#include "lwip/igmp.h"
+
+#include "drv/eth/eth_drv.h"
+#include "mbed.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+#include "netCfg.h"
+#if NET_ETH
+
+EthernetNetIf::EthernetNetIf(const char* hostname) : LwipNetIf(), m_ethArpTimer(), m_dhcpCoarseTimer(), m_dhcpFineTimer(), m_igmpTimer(), m_pNetIf(NULL),
+m_netmask(255,255,255,255), m_gateway(), m_hostname(hostname)
+{
+ //m_hostname = NULL;
+ m_pNetIf = new netif;
+ m_useDhcp = true;
+ m_pDhcp = new dhcp;
+ m_setup = false;
+}
+
+EthernetNetIf::EthernetNetIf(IpAddr ip, IpAddr netmask, IpAddr gateway, IpAddr dns) : LwipNetIf(), m_ethArpTimer(), m_dhcpCoarseTimer(), m_dhcpFineTimer(), m_igmpTimer(), m_pNetIf(NULL), m_hostname(NULL) //W/o DHCP
+{
+ m_hostname = NULL;
+ m_netmask = netmask;
+ m_gateway = gateway;
+ m_ip = ip;
+ m_pNetIf = new netif;
+ dns_setserver(0, &dns.getStruct());
+ m_useDhcp = false;
+ m_setup = false;
+}
+
+EthernetNetIf::~EthernetNetIf()
+{
+ if(m_pNetIf)
+ {
+ igmp_stop(m_pNetIf); //Stop IGMP processing
+ netif_set_down(m_pNetIf);
+ netif_remove(m_pNetIf);
+ delete m_pNetIf;
+ eth_free();
+ }
+
+ if (m_pDhcp)
+ delete m_pDhcp;
+}
+
+EthernetErr EthernetNetIf::setup(int timeout_ms /*= 15000*/)
+{
+ if (m_setup)
+ {
+ igmp_stop(m_pNetIf);
+ netif_set_down(m_pNetIf);
+ netif_remove(m_pNetIf);
+ delete m_pNetIf;
+ eth_free();
+ m_pNetIf = new netif;
+ }
+
+ LwipNetIf::init();
+ //m_ethArpTicker.attach_us(ðarp_tmr, ARP_TMR_INTERVAL * 1000); // = 5s in etharp.h
+ m_ethArpTimer.start();
+ if(m_useDhcp)
+ {
+ //m_dhcpCoarseTicker.attach(&dhcp_coarse_tmr, DHCP_COARSE_TIMER_SECS); // = 60s in dhcp.h
+ //m_dhcpFineTicker.attach_us(&dhcp_fine_tmr, DHCP_FINE_TIMER_MSECS * 1000); // = 500ms in dhcp.h
+ m_dhcpCoarseTimer.start();
+ m_dhcpFineTimer.start();
+ }
+ m_pNetIf->hwaddr_len = ETHARP_HWADDR_LEN; //6
+ eth_address((char *)m_pNetIf->hwaddr);
+
+ DBG("HW Addr is : %02x:%02x:%02x:%02x:%02x:%02x.\n",
+ m_pNetIf->hwaddr[0], m_pNetIf->hwaddr[1], m_pNetIf->hwaddr[2],
+ m_pNetIf->hwaddr[3], m_pNetIf->hwaddr[4], m_pNetIf->hwaddr[5]);
+
+ m_pNetIf = netif_add(m_pNetIf, &(m_ip.getStruct()), &(m_netmask.getStruct()), &(m_gateway.getStruct()), NULL, eth_init, ip_input);//ethernet_input);// ip_input);
+ m_pNetIf->hostname = (char *)m_hostname;
+ netif_set_default(m_pNetIf);
+
+ //DBG("\r\nStarting DHCP.\r\n");
+
+ if(m_useDhcp)
+ {
+ dhcp_set_struct(m_pNetIf, m_pDhcp);
+ dhcp_start(m_pNetIf);
+ DBG("DHCP Started, waiting for IP...\n");
+ }
+ else
+ {
+ netif_set_up(m_pNetIf);
+ }
+
+ Timer timeout;
+ timeout.start();
+ while( !netif_is_up(m_pNetIf) ) //Wait until device is up
+ {
+ if(m_useDhcp)
+ {
+ if(m_dhcpFineTimer.read_ms()>=DHCP_FINE_TIMER_MSECS)
+ {
+ m_dhcpFineTimer.reset();
+ dhcp_fine_tmr();
+ }
+ if(m_dhcpCoarseTimer.read()>=DHCP_COARSE_TIMER_SECS)
+ {
+ m_dhcpCoarseTimer.reset();
+ dhcp_coarse_tmr();
+ }
+ }
+ poll();
+ if( timeout.read_ms() > timeout_ms )
+ {
+ //Abort
+ if(m_useDhcp)
+ dhcp_stop(m_pNetIf);
+ else
+ netif_set_down(m_pNetIf);
+ DBG("\r\nTimeout.\r\n");
+ m_setup = true;
+ return ETH_TIMEOUT;
+ }
+ }
+
+ #if LWIP_IGMP
+ igmp_start(m_pNetIf); //Start IGMP processing
+ #endif
+
+ m_ip = IpAddr(&(m_pNetIf->ip_addr));
+
+ DBG("Connected, IP : %d.%d.%d.%d\n", m_ip[0], m_ip[1], m_ip[2], m_ip[3]);
+
+ m_setup = true;
+ return ETH_OK;
+}
+
+void EthernetNetIf::poll()
+{
+ if(m_ethArpTimer.read_ms()>=ARP_TMR_INTERVAL)
+ {
+ m_ethArpTimer.reset();
+ etharp_tmr();
+ }
+ #if LWIP_IGMP
+ if(m_igmpTimer.read_ms()>=IGMP_TMR_INTERVAL)
+ {
+ m_igmpTimer.reset();
+ igmp_tmr();
+ }
+ #endif
+ LwipNetIf::poll();
+ eth_poll();
+}
+
+const char* EthernetNetIf::getHwAddr() const {
+ return (char*)m_pNetIf->hwaddr;
+}
+
+const char* EthernetNetIf::getHostname() const {
+ return m_hostname;
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/eth/EthernetNetIf.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,109 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+/** \file
+Ethernet network interface header file
+*/
+
+#ifndef ETHERNETNETIF_H
+#define ETHERNETNETIF_H
+
+struct netif;
+
+#include "mbed.h"
+
+#include "if/lwip/LwipNetIf.h"
+#include "lwip/dhcp.h"
+
+///Ethernet network interface return codes
+enum EthernetErr
+{
+ __ETH_MIN = -0xFFFF,
+ ETH_TIMEOUT, ///<Timeout during setup
+ ETH_OK = 0 ///<Success
+};
+
+///Ethernet network interface
+/**
+This class provides Ethernet connectivity to the stack
+*/
+class EthernetNetIf : public LwipNetIf
+{
+public:
+ ///Instantiates the Interface and register it against the stack, DHCP will be used
+ /**
+ * An optional hostname can be specified which will be passed to the DHCP server.
+ * Examples without and with hostname specified:
+ *
+ * @code
+ * EthernetNetIf eth();
+ * @endcode
+ * @code
+ * EthernetNetIf eth("mbedSE");
+ * @endcode
+ */
+ EthernetNetIf(const char* hostname = NULL); //W/ DHCP
+
+ ///Instantiates the Interface and register it against the stack, DHCP will not be used
+ /**
+ IpAddr is a container class that can be constructed with either 4 bytes or no parameters for a null IP address.
+ */
+ EthernetNetIf(IpAddr ip, IpAddr netmask, IpAddr gateway, IpAddr dns); //W/o DHCP
+ virtual ~EthernetNetIf();
+
+ ///Brings the interface up
+ /**
+ Uses DHCP if necessary
+ @param timeout_ms : You can set the timeout parameter in milliseconds, if not it defaults to 15s
+ @return : ETH_OK on success or ETH_TIMEOUT on timeout
+ */
+ EthernetErr setup(int timeout_ms = 15000);
+
+ virtual void poll();
+
+ ///Returns an array containing the hardware address
+ const char* getHwAddr() const;
+
+ ///Returns a pointer to the hostname set in the constructor
+ const char* getHostname() const;
+
+private:
+ Timer m_ethArpTimer;
+ Timer m_dhcpCoarseTimer;
+ Timer m_dhcpFineTimer;
+ Timer m_igmpTimer;
+
+ bool m_useDhcp;
+
+ netif* m_pNetIf;
+
+ IpAddr m_netmask;
+ IpAddr m_gateway;
+
+ const char* m_hostname;
+ dhcp* m_pDhcp;
+ bool m_setup;
+};
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/gprsmodule/GPRSModuleNetIf.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,112 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "netCfg.h"
+#if NET_GPRS_MODULE
+
+#include "GPRSModuleNetIf.h"
+
+#define __DEBUG
+#include "dbg/dbg.h"
+
+GPRSModuleNetIf::GPRSModuleNetIf(PinName tx, PinName rx, int baud /*= 115200*/) : PPPNetIf(NULL), m_serial(tx, rx)
+{
+ PPPNetIf::m_pIf = new GPRSModem();
+ m_serial.baud(baud);
+}
+
+GPRSModuleNetIf::~GPRSModuleNetIf()
+{
+ delete PPPNetIf::m_pIf;
+}
+
+PPPErr GPRSModuleNetIf::connect(const char* apn /*= NULL*/, const char* userId /*= NULL*/, const char* password /*= NULL*/) //Connect using GPRS
+{
+
+ DBG("Powering on module.\n")
+ if(!setOn()) //Could not power on module
+ {
+ DBG("Could not power on module.\n");
+ return PPP_MODEM;
+ }
+
+ //wait(10); //Wait for module to init.
+
+ ATErr atErr;
+ for(int i=0; i<3; i++)
+ {
+ atErr = m_pIf->open(&m_serial); //3 tries
+ if(!atErr)
+ break;
+ DBG("Could not open AT If, trying again.\n");
+ wait(4);
+ }
+
+ if(atErr)
+ {
+ setOff();
+ return PPP_MODEM;
+ }
+
+ DBG("AT If opened.\n");
+
+ PPPErr pppErr;
+ for(int i=0; i<3; i++)
+ {
+ DBG("Trying to connect.\n");
+ pppErr = PPPNetIf::GPRSConnect(apn, userId, password);
+ if(!pppErr)
+ break;
+ DBG("Could not connect.\n");
+ wait(4);
+ }
+ if(pppErr)
+ {
+ setOff();
+ return pppErr;
+ }
+
+ DBG("Connected.\n");
+
+ return PPP_OK;
+}
+
+PPPErr GPRSModuleNetIf::disconnect()
+{
+ DBG("Disconnecting...\n");
+ PPPErr pppErr = PPPNetIf::disconnect();
+ if(pppErr)
+ return pppErr;
+
+ m_pIf->close();
+
+ DBG("Powering off module.\n")
+ setOff(); //Power off module
+
+ DBG("Off.\n")
+
+ return PPP_OK;
+}
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/gprsmodule/GPRSModuleNetIf.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,53 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef GPRSMODULENETIF_H
+#define GPRSMODULENETIF_H
+
+#include "mbed.h"
+
+#include "core/net.h"
+#include "if/ppp/PPPNetIf.h"
+
+#include "drv/gprs/GPRSModem.h"
+
+class GPRSModuleNetIf : protected PPPNetIf
+{
+public:
+ GPRSModuleNetIf(PinName tx, PinName rx, int baud = 115200);
+ virtual ~GPRSModuleNetIf();
+
+ PPPErr connect(const char* apn = NULL, const char* userId = NULL, const char* password = NULL); //Connect using GPRS
+ PPPErr disconnect();
+
+protected:
+ virtual bool setOn() = 0; //True on success
+ virtual bool setOff() = 0; //True on success
+
+private:
+ Serial m_serial;
+
+};
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/gprsmodule/HuaweiGTM900NetIf.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,55 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "netCfg.h"
+#if NET_GPRS_MODULE
+
+#include "HuaweiGTM900NetIf.h"
+
+#define __DEBUG
+#include "dbg/dbg.h"
+
+HuaweiGTM900NetIf::HuaweiGTM900NetIf(PinName tx, PinName rx, int baud /*= 115200*/) : GPRSModuleNetIf(tx, rx, baud)
+{
+
+}
+
+HuaweiGTM900NetIf::~HuaweiGTM900NetIf()
+{
+
+}
+
+bool HuaweiGTM900NetIf::setOn()
+{
+ wait(5); //Wait for module to init.
+
+ return true;
+}
+
+bool HuaweiGTM900NetIf::setOff()
+{
+ return true;
+}
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/gprsmodule/HuaweiGTM900NetIf.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,44 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef HUAWEIGTM900NETIF_H
+#define HUAWEIGTM900NETIF_H
+
+#include "mbed.h"
+
+#include "GPRSModuleNetIf.h"
+
+class HuaweiGTM900NetIf : public GPRSModuleNetIf
+{
+public:
+ HuaweiGTM900NetIf(PinName tx, PinName rx, int baud = 115200);
+ virtual ~HuaweiGTM900NetIf();
+
+protected:
+ virtual bool setOn(); //True on success
+ virtual bool setOff(); //True on success
+
+};
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/gprsmodule/TelitModuleNetIf.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,71 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "netCfg.h"
+#if NET_GPRS_MODULE
+
+#include "TelitModuleNetIf.h"
+
+#define __DEBUG
+#include "dbg/dbg.h"
+
+TelitModuleNetIf::TelitModuleNetIf(PinName tx, PinName rx, PinName pwrSetPin, PinName pwrMonPin, int baud /*= 115200*/) : GPRSModuleNetIf(tx, rx, baud), m_module(pwrSetPin, pwrMonPin)
+{
+
+}
+
+TelitModuleNetIf::~TelitModuleNetIf()
+{
+
+}
+
+bool TelitModuleNetIf::setOn()
+{
+ if(m_module.isOn())
+ {
+ DBG("Resetting module...\n");
+ m_module.off(); //Reset module if needed
+ }
+
+ wait(4);
+
+ DBG("Powering on module.\n")
+ if(!m_module.on()) //Could not power on module
+ {
+ DBG("Could not power on module.\n");
+ return false;
+ }
+
+ wait(10); //Wait for module to init.
+
+ return true;
+}
+
+bool TelitModuleNetIf::setOff()
+{
+ DBG("Powering off module.\n")
+ return m_module.off(); //Power off module
+}
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/gprsmodule/TelitModuleNetIf.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,49 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef TELITMODULENETIF_H
+#define TELITMODULENETIF_H
+
+#include "mbed.h"
+
+#include "GPRSModuleNetIf.h"
+
+#include "drv/gprsmodule/TelitModule.h"
+
+class TelitModuleNetIf : public GPRSModuleNetIf
+{
+public:
+ TelitModuleNetIf(PinName tx, PinName rx, PinName pwrSetPin, PinName pwrMonPin, int baud = 115200);
+ virtual ~TelitModuleNetIf();
+
+protected:
+ virtual bool setOn(); //True on success
+ virtual bool setOff(); //True on success
+
+private:
+ TelitModule m_module;
+
+};
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/lwip/LwipNetIf.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,98 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "LwipNetIf.h"
+#include "lwip/init.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/dns.h"
+
+#include "lwipNetTcpSocket.h"
+#include "lwipNetUdpSocket.h"
+#include "lwipNetDnsRequest.h"
+
+#include "netCfg.h"
+#if NET_LWIP_STACK
+
+//See doc/rawapi.txt for details
+
+LwipNetIf::LwipNetIf() : NetIf(), m_tcpTimer(), m_dnsTimer(), m_init(false)
+{
+
+}
+
+
+LwipNetIf::~LwipNetIf()
+{
+
+}
+
+void LwipNetIf::init()
+{
+ if(m_init)
+ return;
+ m_init=true;
+ lwip_init(); //init lwip, see init.c for details
+
+ //Setup Clocks
+ m_tcpTimer.start();
+ m_dnsTimer.start();
+ //m_tcpTicker.attach_us( tcp_tmr, TCP_TMR_INTERVAL * 1000 ); //TCP_TMR_INTERVAL = 250 ms in tcp_impl.h
+ //m_dnsTicker.attach_us( dns_tmr, DNS_TMR_INTERVAL * 1000 ); //DNS_TMR_INTERVAL = 1000 ms in dns.h
+}
+
+NetTcpSocket* LwipNetIf::tcpSocket() //Create a new tcp socket
+{
+ return new LwipNetTcpSocket();
+}
+
+NetUdpSocket* LwipNetIf::udpSocket() //Create a new udp socket
+{
+ return new LwipNetUdpSocket();
+}
+
+NetDnsRequest* LwipNetIf::dnsRequest(const char* hostname) //Create a new NetDnsRequest object
+{
+ return new LwipNetDnsRequest(hostname);
+}
+
+NetDnsRequest* LwipNetIf::dnsRequest(Host* pHost) //Create a new NetDnsRequest object
+{
+ return new LwipNetDnsRequest(pHost);
+}
+
+void LwipNetIf::poll()
+{
+ if(m_init && m_tcpTimer.read_ms() >= TCP_TMR_INTERVAL)
+ {
+ m_tcpTimer.reset();
+ tcp_tmr(); //Poll LwIP
+ }
+ if(m_init && m_dnsTimer.read_ms() >= DNS_TMR_INTERVAL)
+ {
+ m_dnsTimer.reset();
+ dns_tmr();
+ }
+ //Do some stuff...
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/lwip/LwipNetIf.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,62 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef LWIPNETIF_H
+#define LWIPNETIF_H
+
+//class Ticker;
+#include "mbed.h"
+
+#define NET_LWIP_STACK 1
+#include "core/net.h"
+#include "if/net/netif.h"
+/*
+#include "lwipNetTcpSocket.h"
+#include "lwipNetUdpSocket.h"
+#include "lwipNetDnsRequest.h"
+*/
+
+class LwipNetIf : public NetIf
+{
+public:
+ LwipNetIf();
+ virtual ~LwipNetIf();
+
+ void init();
+
+ virtual NetTcpSocket* tcpSocket(); //Create a new tcp socket
+ virtual NetUdpSocket* udpSocket(); //Create a new udp socket
+ virtual NetDnsRequest* dnsRequest(const char* hostname); //Create a new NetDnsRequest object
+ virtual NetDnsRequest* dnsRequest(Host* pHost); //Create a new NetDnsRequest object
+
+ virtual void poll();
+
+private:
+ Timer m_tcpTimer;
+ Timer m_dnsTimer;
+
+ bool m_init;
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/lwip/lwipNetDnsRequest.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,152 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "lwipNetDnsRequest.h"
+#include "lwip/err.h" //err_t, ERR_xxx
+#include "lwip/dns.h"
+
+#include "netCfg.h"
+#if NET_LWIP_STACK
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+LwipNetDnsRequest::LwipNetDnsRequest(const char* hostname) : NetDnsRequest(hostname), m_state(LWIPNETDNS_START), m_cbFired(false), m_closing(false)
+{
+ DBG("New LwipNetDnsRequest %p\n", this);
+}
+
+LwipNetDnsRequest::LwipNetDnsRequest(Host* pHost) : NetDnsRequest(pHost), m_state(LWIPNETDNS_START), m_cbFired(false), m_closing(false)
+{
+ DBG("New LwipNetDnsRequest %p\n", this);
+}
+
+LwipNetDnsRequest::~LwipNetDnsRequest()
+{
+ DBG("LwipNetDnsRequest %p destroyed\n", this);
+}
+
+/*
+Main useful function here (see lwip/dns.h):
+err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr,
+ dns_found_callback found, void *callback_arg);
+*/
+
+//Execute request & return OK if found, NOTFOUND or ERROR on error, or PROCESSING if the request has not completed yet
+void LwipNetDnsRequest::poll()
+{
+ err_t err;
+ switch(m_state)
+ {
+ case LWIPNETDNS_START: //First req, let's call dns_gethostbyname
+ ip_addr_t ipStruct;
+ err = dns_gethostbyname(m_hostname, &ipStruct, LwipNetDnsRequest::sFoundCb, (void*) this );
+ if( err == ERR_OK )
+ {
+ m_ip = IpAddr(&ipStruct);
+ m_state = LWIPNETDNS_OK;
+ DBG("DNS: Ip found in cache.\n");
+ }
+ else if( err == ERR_INPROGRESS)
+ {
+ DBG("DNS: Processing.\n");
+ m_state = LWIPNETDNS_PROCESSING;
+ }
+ else //Likely ERR_VAL
+ {
+ DBG("DNS: Error on init.\n");
+ m_state = LWIPNETDNS_ERROR;
+ }
+ break;
+ case LWIPNETDNS_PROCESSING:
+ break; //Nothing to do, DNS is polled on interrupt
+ case LWIPNETDNS_OK:
+ if(!m_cbFired)
+ {
+ DBG("DNS: Ip found.\n");
+ m_cbFired = true;
+ onReply(NETDNS_FOUND); //Raise callback
+ }
+ break;
+ case LWIPNETDNS_NOTFOUND:
+ if(!m_cbFired)
+ {
+ DBG("DNS: could not be resolved.\n");
+ m_cbFired = true;
+ onReply(NETDNS_NOTFOUND); //Raise callback
+ }
+ break;
+ case LWIPNETDNS_ERROR:
+ default:
+ if(!m_cbFired)
+ {
+ DBG("DNS: Error.\n");
+ m_cbFired = true;
+ onReply(NETDNS_ERROR); //Raise callback
+ }
+ break;
+ }
+ if(m_closing && (m_state!=LWIPNETDNS_PROCESSING)) //Check wether the closure has been reqd
+ {
+ DBG("LwipNetDnsRequest: Closing in poll()\n");
+ NetDnsRequest::close();
+ }
+}
+
+void LwipNetDnsRequest::close()
+{
+ DBG("LwipNetDnsRequest: Close req\n");
+ if(m_state!=LWIPNETDNS_PROCESSING)
+ {
+ DBG("LwipNetDnsRequest: Closing in close()\n");
+ NetDnsRequest::close();
+ }
+ else //Cannot close rightaway, waiting for callback from underlying layer
+ {
+ m_closing = true;
+ }
+}
+
+void LwipNetDnsRequest::foundCb(const char *name, ip_addr_t *ipaddr)
+{
+ if( ipaddr == NULL )
+ {
+ DBG("LwipNetDnsRequest: Callback: Name not found\n");
+ m_state = LWIPNETDNS_NOTFOUND;
+ return;
+ }
+ DBG("LwipNetDnsRequest: Callback: Resolved\n");
+ m_ip = IpAddr(ipaddr);
+ m_state = LWIPNETDNS_OK;
+}
+
+
+void LwipNetDnsRequest::sFoundCb(const char *name, ip_addr_t *ipaddr, void *arg)
+{
+ DBG("LwipNetDnsRequest: Static callback\n");
+ LwipNetDnsRequest* pMe = (LwipNetDnsRequest*) arg;
+ return pMe->foundCb( name, ipaddr );
+}
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/lwip/lwipNetDnsRequest.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,67 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef LWIPNETDNSREQUEST_H
+#define LWIPNETDNSREQUEST_H
+
+#define NET_LWIP_STACK 1
+#include "if/net/netdnsrequest.h"
+
+//struct ip_addr_t;
+#include "lwip/ip_addr.h"
+
+class LwipNetDnsRequest : public NetDnsRequest
+{
+public:
+ LwipNetDnsRequest(const char* hostname);
+ LwipNetDnsRequest(Host* pHost);
+ virtual ~LwipNetDnsRequest();
+
+ //Execute request & return OK if found, NOTFOUND or ERROR on error, or PROCESSING if the request has not completed yet
+ virtual void poll();
+
+ virtual void close();
+
+protected:
+ void foundCb(const char *name, ip_addr_t *ipaddr);
+
+private:
+ enum LwipNetDnsState
+ {
+ LWIPNETDNS_START,
+ LWIPNETDNS_PROCESSING, //Req has not completed
+ LWIPNETDNS_NOTFOUND,
+ LWIPNETDNS_ERROR,
+ LWIPNETDNS_OK
+ };
+
+ LwipNetDnsState m_state;
+ bool m_cbFired;
+
+ //Static callbacks : Transforms into a C++ callback
+ static void sFoundCb(const char *name, ip_addr_t *ipaddr, void *arg);
+
+ bool m_closing;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/lwip/lwipNetTcpSocket.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,456 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "lwipNetTcpSocket.h"
+#include "lwip/tcp.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+#include "netCfg.h"
+#if NET_LWIP_STACK
+
+LwipNetTcpSocket::LwipNetTcpSocket(tcp_pcb* pPcb /*= NULL*/) : NetTcpSocket(), m_pPcb(pPcb), m_lpInNetTcpSocket(), //Passes a pcb if already created (by an accept req for instance), in that case transfers ownership
+m_pReadPbuf(NULL)
+{
+ DBG("New NetTcpSocket %p\n", (void*)this);
+ if(!m_pPcb)
+ {
+ m_pPcb = tcp_new();
+ DBG("Creating new PCB %p\n", m_pPcb);
+ }
+ if(m_pPcb)
+ {
+ //Setup callbacks
+ tcp_arg( (tcp_pcb*) m_pPcb, (void*) this ); //this will be passed to each static callback
+
+ tcp_recv( (tcp_pcb*) m_pPcb, LwipNetTcpSocket::sRecvCb );
+ tcp_sent((tcp_pcb*) m_pPcb, LwipNetTcpSocket::sSentCb );
+ tcp_err( (tcp_pcb*) m_pPcb, LwipNetTcpSocket::sErrCb );
+ //Connected callback is defined in connect()
+ //Accept callback is defined in listen()
+ DBG("NetTcpSocket created.\n");
+ }
+}
+
+LwipNetTcpSocket::~LwipNetTcpSocket()
+{
+/* if(m_pPcb)
+ tcp_close( (tcp_pcb*) m_pPcb); //Disconnect & free pcb*/
+ close();
+}
+
+NetTcpSocketErr LwipNetTcpSocket::bind(const Host& me)
+{
+ if(!m_pPcb)
+ return NETTCPSOCKET_MEM; //NetTcpSocket was not properly initialised, should destroy it & retry
+
+ err_t err = tcp_bind( (tcp_pcb*) m_pPcb, IP_ADDR_ANY, me.getPort()); //IP_ADDR_ANY : Bind the connection to all local addresses
+ if(err)
+ return NETTCPSOCKET_INUSE;
+
+ return NETTCPSOCKET_OK;
+}
+
+NetTcpSocketErr LwipNetTcpSocket::listen()
+{
+ if(!m_pPcb)
+ return NETTCPSOCKET_MEM; //NetTcpSocket was not properly initialised, should destroy it & retry
+/*
+ From doc/rawapi.txt :
+
+ The tcp_listen() function returns a new connection identifier, and
+ the one passed as an argument to the function will be
+ deallocated. The reason for this behavior is that less memory is
+ needed for a connection that is listening, so tcp_listen() will
+ reclaim the memory needed for the original connection and allocate a
+ new smaller memory block for the listening connection.
+*/
+
+// tcp_pcb* pNewPcb = tcp_listen(m_pPcb);
+ tcp_pcb* pNewPcb = tcp_listen_with_backlog((tcp_pcb*)m_pPcb, 5);
+ if( !pNewPcb ) //Not enough memory to create the listening pcb
+ return NETTCPSOCKET_MEM;
+
+ m_pPcb = pNewPcb;
+
+ tcp_accept( (tcp_pcb*) m_pPcb, LwipNetTcpSocket::sAcceptCb );
+
+ return NETTCPSOCKET_OK;
+}
+
+NetTcpSocketErr LwipNetTcpSocket::connect(const Host& host)
+{
+ if(!m_pPcb)
+ return NETTCPSOCKET_MEM; //NetTcpSocket was not properly initialised, should destroy it & retry
+
+ ip_addr_t ip = host.getIp().getStruct();
+ err_t err = tcp_connect( (tcp_pcb*) m_pPcb, &ip, host.getPort(), LwipNetTcpSocket::sConnectedCb );
+
+ if(err)
+ return NETTCPSOCKET_MEM;
+
+ return NETTCPSOCKET_OK;
+}
+
+NetTcpSocketErr LwipNetTcpSocket::accept(Host* pClient, NetTcpSocket** ppNewNetTcpSocket)
+{
+ if( !m_pPcb ) //Pcb doesn't exist (anymore)
+ return NETTCPSOCKET_MEM;
+ //Dequeue a connection
+ //if( m_lpInPcb.empty() )
+ if( m_lpInNetTcpSocket.empty() )
+ return NETTCPSOCKET_EMPTY;
+
+ tcp_accepted( ((tcp_pcb*) m_pPcb) ); //Should fire proper events //WARN: m_pPcb is the GOOD param here (and not pInPcb)
+
+/* tcp_pcb* pInPcb = m_lpInPcb.front();
+ m_lpInPcb.pop();*/
+
+ if( (m_lpInNetTcpSocket.front()) == NULL )
+ {
+ m_lpInNetTcpSocket.pop();
+ return NETTCPSOCKET_RST;
+ }
+
+ if( (m_lpInNetTcpSocket.front())->m_closed )
+ {
+ Net::releaseTcpSocket(m_lpInNetTcpSocket.front());
+ m_lpInNetTcpSocket.pop();
+ return NETTCPSOCKET_RST;
+ }
+
+ ip_addr_t* ip = (ip_addr_t*) &( (m_lpInNetTcpSocket.front()->m_pPcb)->remote_ip);
+
+ *ppNewNetTcpSocket = m_lpInNetTcpSocket.front();
+ *pClient = Host(
+ IpAddr(
+ ip
+ ),
+ m_lpInNetTcpSocket.front()->m_pPcb->remote_port
+ );
+ m_lpInNetTcpSocket.pop();
+// *pClient = Host( IpAddr(pInPcb->remote_ip), pInPcb->remote_port );
+
+ //Return a new socket
+ // *ppNewNetTcpSocket = (NetTcpSocket*) new LwipNetTcpSocket(pInPcb);
+
+ //tcp_accepted( ((tcp_pcb*) m_pPcb) ); //Should fire proper events //WARN: m_pPcb is the GOOD param here (and not pInPcb)
+
+/* if(*ppNewNetTcpSocket == NULL)
+ {
+ DBG("Not enough mem, socket dropped in LwipNetTcpSocket::accept.\n");
+ tcp_abort(pInPcb);
+ }*/
+
+ return NETTCPSOCKET_OK;
+}
+
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+int /*if < 0 : NetTcpSocketErr*/ LwipNetTcpSocket::send(const char* buf, int len)
+{
+ if( !m_pPcb ) //Pcb doesn't exist (anymore)
+ return NETTCPSOCKET_MEM;
+ int outLen = MIN( len, tcp_sndbuf( (tcp_pcb*) m_pPcb) );
+ //tcp_sndbuf() returns the number of bytes available in the output queue, so never go above it
+ err_t err = tcp_write( (tcp_pcb*) m_pPcb, (void*) buf, outLen, TCP_WRITE_FLAG_COPY );
+ //Flags are TCP_WRITE_FLAG_COPY & TCP_WRITE_FLAG_MORE (see tcp_out.c) :
+ //If TCP_WRITE_FLAG_MORE is not set ask client to push buffered data to app
+ if(err)
+ {
+ switch( err )
+ {
+ case ERR_CONN:
+ return (int) NETTCPSOCKET_SETUP; //Not connected properly
+ case ERR_ARG:
+ return (int) NETTCPSOCKET_SETUP; //Wrong args ! (like buf pointing to NULL)
+ case ERR_MEM:
+ default:
+ return (int) NETTCPSOCKET_MEM; //Not enough memory
+ }
+ }
+ return outLen;
+}
+
+int /*if < 0 : NetTcpSocketErr*/ LwipNetTcpSocket::recv(char* buf, int len)
+{
+ if( !m_pPcb ) //Pcb doesn't exist (anymore)
+ return NETTCPSOCKET_MEM;
+ int inLen = 0;
+ int cpyLen = 0;
+
+ static int rmgLen = 0;
+ //Contains the remaining len in this pbuf
+
+ if( !m_pReadPbuf )
+ {
+ rmgLen = 0;
+ return 0;
+ }
+
+ if ( !rmgLen ) //We did not know m_pReadPbuf->len last time we called this fn
+ {
+ rmgLen = m_pReadPbuf->len;
+ }
+
+ while ( inLen < len )
+ {
+ cpyLen = MIN( (len - inLen), rmgLen ); //Remaining len to copy, remaining len in THIS pbuf
+ memcpy((void*)buf, (void*)((char*)(m_pReadPbuf->payload) + (m_pReadPbuf->len - rmgLen)), cpyLen);
+ inLen += cpyLen;
+ buf += cpyLen;
+
+ rmgLen = rmgLen - cpyLen; //Update rmgLen
+
+ if( rmgLen > 0 )
+ {
+ //We did not read this pbuf completely, so let's save it's pos & return
+ break;
+ }
+
+ if(m_pReadPbuf->next)
+ {
+ pbuf* pNextPBuf = m_pReadPbuf->next;
+ m_pReadPbuf->next = NULL; //So that it is not freed as well
+ //We get the reference to pNextPBuf from m_pReadPbuf
+ pbuf_free((pbuf*)m_pReadPbuf);
+ m_pReadPbuf = pNextPBuf;
+ rmgLen = m_pReadPbuf->len;
+ }
+ else
+ {
+ pbuf_free((pbuf*)m_pReadPbuf);
+ m_pReadPbuf = NULL;
+ rmgLen = 0;
+ break; //No more data to read
+ }
+
+ }
+
+ //tcp_recved(m_pPcb, inLen); //Acknowledge the reception
+
+ return inLen;
+}
+
+NetTcpSocketErr LwipNetTcpSocket::close()
+{
+ //DBG("LwipNetTcpSocket::close() : Closing...\n");
+
+ if(m_closed)
+ return NETTCPSOCKET_OK; //Already being closed
+ m_closed = true;
+
+ if( !m_pPcb ) //Pcb doesn't exist (anymore)
+ return NETTCPSOCKET_MEM;
+
+ //Cleanup incoming data
+ cleanUp();
+
+ if( !!tcp_close( (tcp_pcb*) m_pPcb) )
+ {
+ DBG("LwipNetTcpSocket::close() could not close properly, abort.\n");
+ tcp_abort( (tcp_pcb*) m_pPcb);
+ m_pPcb = NULL;
+ return NETTCPSOCKET_MEM;
+ }
+
+ DBG("LwipNetTcpSocket::close() : connection closed successfully.\n");
+
+ m_pPcb = NULL;
+ return NETTCPSOCKET_OK;
+}
+
+NetTcpSocketErr LwipNetTcpSocket::poll()
+{
+ NetTcpSocket::flushEvents();
+ return NETTCPSOCKET_OK;
+}
+
+// Callbacks events
+
+err_t LwipNetTcpSocket::acceptCb(struct tcp_pcb *newpcb, err_t err)
+{
+ if(err)
+ {
+ DBG("Error %d in LwipNetTcpSocket::acceptCb.\n", err);
+ return err;
+ }
+ //FIXME: MEM Errs
+ //m_lpInPcb.push(newpcb); //Add connection to the queue
+ LwipNetTcpSocket* pNewNetTcpSocket = new LwipNetTcpSocket(newpcb);
+
+ if(pNewNetTcpSocket == NULL)
+ {
+ DBG("Not enough mem, socket dropped in LwipNetTcpSocket::acceptCb.\n");
+ tcp_abort(newpcb);
+ return ERR_ABRT;
+ }
+
+ pNewNetTcpSocket->m_refs++;
+ m_lpInNetTcpSocket.push( pNewNetTcpSocket );
+
+ // tcp_accepted(newpcb);
+ // tcp_accepted( m_pPcb ); //Should fire proper events //WARN: m_pPcb is the GOOD param here (and not pInPcb)
+ queueEvent(NETTCPSOCKET_ACCEPT);
+ return ERR_OK;
+}
+
+err_t LwipNetTcpSocket::connectedCb(struct tcp_pcb *tpcb, err_t err)
+{
+ queueEvent(NETTCPSOCKET_CONNECTED);
+ return ERR_OK;
+}
+
+void LwipNetTcpSocket::errCb(err_t err)
+{
+ DBG("NetTcpSocket %p - Error %d in LwipNetTcpSocket::errCb.\n", (void*)this, err);
+ //WARN: At this point, m_pPcb has been freed by lwIP
+ m_pPcb = NULL;
+ //These errors are fatal, discard all events queued before so that the errors are handled first
+ discardEvents();
+ m_closed = true;
+ cleanUp();
+ if( err == ERR_ABRT)
+ queueEvent(NETTCPSOCKET_CONABRT);
+ else //if( err == ERR_RST)
+ queueEvent(NETTCPSOCKET_CONRST);
+}
+
+err_t LwipNetTcpSocket::sentCb(tcp_pcb* tpcb, u16_t len)
+{
+// DBG("%d bytes ACKed by host.\n", len);
+ queueEvent(NETTCPSOCKET_WRITEABLE);
+ return ERR_OK;
+}
+
+err_t LwipNetTcpSocket::recvCb(tcp_pcb* tpcb, pbuf *p, err_t err)
+{
+ //Store pbuf ptr
+ // DBG("Receive CB with err = %d & len = %d.\n", err, p->tot_len);
+// tcp_recved( (tcp_pcb*) m_pPcb, p->tot_len); //Acknowledge the reception
+
+ if(err)
+ {
+ queueEvent(NETTCPSOCKET_ERROR);
+ return ERR_OK; //FIXME: More robust error handling there
+ }
+ else if(!p)
+ {
+ DBG("NetTcpSocket %p - Connection closed by remote host (LwipNetTcpSocket::recvCb).\n", (void*)this);
+ //Buf is NULL, that means that the connection has been closed by remote host
+
+ //FIX: 27/05/2010: We do not want to deallocate the socket while some data might still be readable
+ //REMOVED: close();
+
+ //However we do not want to close the socket yet
+
+ queueEvent(NETTCPSOCKET_DISCONNECTED);
+ return ERR_OK;
+ }
+
+ //We asserted that p is a valid pointer
+
+ //New data processing
+ tcp_recved( tpcb, p->tot_len); //Acknowledge the reception
+ if(!m_pReadPbuf)
+ {
+ m_pReadPbuf = p;
+ queueEvent(NETTCPSOCKET_READABLE);
+ }
+ else
+ {
+ pbuf_cat((pbuf*)m_pReadPbuf, p); //m_pReadPbuf is not empty, tail p to it and drop our ref
+ //No need to queue an event in that case since the read buf has not been processed yet
+ }
+ return ERR_OK;
+}
+
+
+void LwipNetTcpSocket::cleanUp() //Flush input buffer
+{
+ //Ensure that further error won't be followed to this inst (which can be destroyed)
+ if( m_pPcb )
+ {
+ tcp_arg( (tcp_pcb*) m_pPcb, (void*) NULL );
+ tcp_recv( (tcp_pcb*) m_pPcb, NULL );
+ tcp_sent((tcp_pcb*) m_pPcb, NULL );
+ tcp_err( (tcp_pcb*) m_pPcb, NULL );
+ }
+
+ if( m_pReadPbuf )
+ {
+ DBG("Deallocating unread data.\n");
+ pbuf_free((pbuf*)m_pReadPbuf); //Free all unread data
+ m_pReadPbuf = NULL;
+ recv(NULL,0); //Update recv ptr position
+ }
+}
+
+// Static callbacks from LwIp
+
+err_t LwipNetTcpSocket::sAcceptCb(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ LwipNetTcpSocket* pMe = (LwipNetTcpSocket*) arg;
+ return pMe->acceptCb( newpcb, err );
+}
+
+err_t LwipNetTcpSocket::sConnectedCb(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+ LwipNetTcpSocket* pMe = (LwipNetTcpSocket*) arg;
+ return pMe->connectedCb( tpcb, err );
+}
+
+void LwipNetTcpSocket::sErrCb(void *arg, err_t err)
+{
+ if( !arg )
+ {
+ DBG("NetTcpSocket - Error %d in LwipNetTcpSocket::sErrCb.\n", err);
+ return; //The socket has been destroyed, discard error
+ }
+ LwipNetTcpSocket* pMe = (LwipNetTcpSocket*) arg;
+ return pMe->errCb( err );
+}
+
+err_t LwipNetTcpSocket::sSentCb(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+ LwipNetTcpSocket* pMe = (LwipNetTcpSocket*) arg;
+ return pMe->sentCb( tpcb, len );
+}
+
+err_t LwipNetTcpSocket::sRecvCb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+ if( tpcb->flags & TF_RXCLOSED )
+ {
+ //The Pcb is in a closing state
+ //Discard that data here since we might have destroyed the corresponding socket object
+ tcp_recved( tpcb, p->tot_len);
+ pbuf_free( p );
+ return ERR_OK;
+ }
+ LwipNetTcpSocket* pMe = (LwipNetTcpSocket*) arg;
+ return pMe->recvCb( tpcb, p, err );
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/lwip/lwipNetTcpSocket.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,90 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef LWIPNETTCPSOCKET_H
+#define LWIPNETTCPSOCKET_H
+
+#define NET_LWIP_STACK 1
+#include "if/net/nettcpsocket.h"
+#include "LwipNetIf.h"
+
+#include "stdint.h"
+
+//Implements NetTcpSockets over lwIP raw API
+
+struct tcp_pcb; //Represents a Tcp Connection, "Protocol Control Block", see rawapi.txt & tcp.h
+struct pbuf; //Lwip Buffer Container
+
+typedef signed char err_t;
+typedef uint16_t u16_t;
+
+class LwipNetTcpSocket: public NetTcpSocket
+{
+public:
+ LwipNetTcpSocket(tcp_pcb* pPcb = NULL); //Passes a pcb if already created (by an accept req for instance), in that case transfers ownership
+ virtual ~LwipNetTcpSocket();
+
+ virtual NetTcpSocketErr bind(const Host& me);
+ virtual NetTcpSocketErr listen();
+ virtual NetTcpSocketErr connect(const Host& host);
+ virtual NetTcpSocketErr accept(Host* pClient, NetTcpSocket** ppNewNetTcpSocket);
+
+ virtual int /*if < 0 : NetTcpSocketErr*/ send(const char* buf, int len);
+ virtual int /*if < 0 : NetTcpSocketErr*/ recv(char* buf, int len);
+
+ virtual NetTcpSocketErr close();
+
+ virtual NetTcpSocketErr poll();
+
+protected:
+ volatile tcp_pcb* m_pPcb;
+
+ //Events callbacks from lwIp
+ err_t acceptCb(tcp_pcb* newpcb, err_t err);
+ err_t connectedCb(tcp_pcb* tpcb, err_t err);
+
+ void errCb(err_t err);
+
+ err_t sentCb(tcp_pcb* tpcb, u16_t len);
+ err_t recvCb(tcp_pcb* tpcb, pbuf *p, err_t err);
+
+private:
+ void cleanUp(); //Flush input buffer
+
+// queue<tcp_pcb*> m_lpInPcb; //Incoming connections that have not been accepted yet
+ queue<LwipNetTcpSocket*> m_lpInNetTcpSocket; //Incoming connections that have not been accepted yet
+
+ volatile pbuf* m_pReadPbuf; //Ptr to read buffer
+
+ //Static callbacks : Transforms into a C++ callback
+ static err_t sAcceptCb(void *arg, struct tcp_pcb *newpcb, err_t err);
+ static err_t sConnectedCb(void *arg, struct tcp_pcb *tpcb, err_t err);
+
+ static void sErrCb(void *arg, err_t err);
+
+ static err_t sSentCb(void *arg, struct tcp_pcb *tpcb, u16_t len);
+ static err_t sRecvCb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/lwip/lwipNetUdpSocket.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,265 @@
+#pragma diag_remark 1464
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "lwip/ip_addr.h"
+#include "lwipNetUdpSocket.h"
+#include "lwip/udp.h"
+#include "lwip/igmp.h"
+
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+#include "netCfg.h"
+#if NET_LWIP_STACK
+
+LwipNetUdpSocket::LwipNetUdpSocket(udp_pcb* pPcb /*= NULL*/) : NetUdpSocket(), m_pPcb(pPcb), m_lInPkt(), m_multicastGroup() //Passes a pcb if already created (by an accept req for instance), in that case transfers ownership
+{
+ DBG("New LwipNetUdpSocket %p (pPCb=%p)\n", (void*)this, (void*) pPcb);
+ if(!m_pPcb)
+ m_pPcb = udp_new();
+ if(m_pPcb)
+ {
+ //Setup callback
+ udp_recv( (udp_pcb*) m_pPcb, LwipNetUdpSocket::sRecvCb, (void*) this );
+ }
+}
+
+LwipNetUdpSocket::~LwipNetUdpSocket()
+{
+ close();
+}
+
+NetUdpSocketErr LwipNetUdpSocket::bind(const Host& me)
+{
+ err_t err;
+
+ if(!m_pPcb)
+ return NETUDPSOCKET_MEM; //NetUdpSocket was not properly initialised, should destroy it & retry
+
+ #if LWIP_IGMP //Multicast support enabled
+ if(me.getIp().isMulticast())
+ {
+ DBG("This is a multicast addr, joining multicast group\n");
+ m_multicastGroup = me.getIp();
+ err = igmp_joingroup(IP_ADDR_ANY, &(m_multicastGroup.getStruct()));
+ if(err)
+ return NETUDPSOCKET_IF; //Could not find or create group
+ }
+ #endif
+
+ err = udp_bind( (udp_pcb*) m_pPcb, IP_ADDR_ANY, me.getPort()); //IP_ADDR_ANY : Bind the connection to all local addresses
+ if(err)
+ return NETUDPSOCKET_INUSE;
+
+ //Setup callback
+ udp_recv( (udp_pcb*) m_pPcb, LwipNetUdpSocket::sRecvCb, (void*) this );
+
+ return NETUDPSOCKET_OK;
+}
+
+#define MAX(a,b) ((a>b)?a:b)
+#define MIN(a,b) ((a<b)?a:b)
+
+int /*if < 0 : NetUdpSocketErr*/ LwipNetUdpSocket::sendto(const char* buf, int len, Host* pHost)
+{
+ if( !m_pPcb ) //Pcb doesn't exist (anymore)
+ return NETUDPSOCKET_MEM;
+ pbuf* p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_POOL);
+ if( !p )
+ return NETUDPSOCKET_MEM;
+ char* pBuf = (char*) buf;
+ pbuf* q = p;
+ do
+ {
+ memcpy (q->payload, (void*)pBuf, q->len);
+ pBuf += q->len;
+ q = q->next;
+ } while(q != NULL);
+
+ err_t err = udp_sendto( (udp_pcb*) m_pPcb, p, &(pHost->getIp().getStruct()), pHost->getPort() );
+ pbuf_free( p );
+ if(err)
+ return NETUDPSOCKET_SETUP; //Connection problem
+ DBG("%d bytes sent in UDP Socket.\n", len);
+ return len;
+}
+
+int /*if < 0 : NetUdpSocketErr*/ LwipNetUdpSocket::recvfrom(char* buf, int len, Host* pHost)
+{
+ if( !m_pPcb ) //Pcb doesn't exist (anymore)
+ return NETUDPSOCKET_MEM;
+ int inLen = 0;
+ int cpyLen = 0;
+
+ static int rmgLen = 0;
+ //Contains the remaining len in this pbuf
+
+ if( m_lInPkt.empty() )
+ return 0;
+
+ pbuf* pBuf = (pbuf*) m_lInPkt.front().pBuf;
+
+ if(pHost)
+ *pHost = Host( IpAddr(&m_lInPkt.front().addr), m_lInPkt.front().port );
+
+ if( !pBuf )
+ {
+ rmgLen = 0;
+ return 0;
+ }
+
+ if ( !rmgLen ) //We did not know m_pReadPbuf->len last time we called this fn
+ {
+ rmgLen = pBuf->len;
+ }
+
+ while ( inLen < len )
+ {
+ cpyLen = MIN( (len - inLen), rmgLen ); //Remaining len to copy, remaining len in THIS pbuf
+ memcpy((void*)buf, (void*)((char*)(pBuf->payload) + (pBuf->len - rmgLen)), cpyLen);
+ inLen += cpyLen;
+ buf += cpyLen;
+
+ rmgLen = rmgLen - cpyLen; //Update rmgLen
+
+ if( rmgLen > 0 )
+ {
+ //We did not read this pbuf completely, so let's save it's pos & return
+ break;
+ }
+
+ if(pBuf->next)
+ {
+ pbuf* pNextPBuf = pBuf->next;
+ pBuf->next = NULL; //So that it is not freed as well
+ //We get the reference to pNextPBuf from m_pReadPbuf
+ pbuf_free((pbuf*)pBuf);
+ pBuf = pNextPBuf;
+ rmgLen = pBuf->len;
+ }
+ else
+ {
+ pbuf_free((pbuf*)pBuf);
+ pBuf = NULL;
+ rmgLen = 0;
+ m_lInPkt.pop_front();
+ break; //No more data to read
+ }
+ }
+
+ return inLen;
+}
+
+NetUdpSocketErr LwipNetUdpSocket::close()
+{
+ DBG("LwipNetUdpSocket::close() : Closing...\n");
+
+ if(m_closed)
+ return NETUDPSOCKET_OK; //Already being closed
+ m_closed = true;
+
+ if( !m_pPcb ) //Pcb doesn't exist (anymore)
+ return NETUDPSOCKET_MEM;
+
+ DBG("LwipNetUdpSocket::close() : Cleanup...\n");
+
+ //Cleanup incoming data
+ cleanUp();
+
+
+ DBG("LwipNetUdpSocket::close() : removing m_pPcb...\n");
+ udp_remove( (udp_pcb*) m_pPcb);
+
+ m_pPcb = NULL;
+ return NETUDPSOCKET_OK;
+}
+
+NetUdpSocketErr LwipNetUdpSocket::poll()
+{
+ NetUdpSocket::flushEvents();
+ return NETUDPSOCKET_OK;
+}
+
+// Callbacks events
+
+void LwipNetUdpSocket::recvCb(udp_pcb* pcb, struct pbuf* p, ip_addr_t* addr, u16_t port)
+{
+ DBG(" Packet of length %d arrived in UDP Socket.\n", p->tot_len);
+ list<InPacket>::iterator it;
+ for ( it = m_lInPkt.begin(); it != m_lInPkt.end(); it++ )
+ {
+ if( ip_addr_cmp((&((*it).addr)), addr) && ((*it).port == port) )
+ {
+ //Let's tail this packet to the previous one
+ pbuf_cat((pbuf*)((*it).pBuf), p);
+ //No need to queue an event in that case since the read buf has not been processed yet
+ return;
+ }
+ }
+
+ //New host, add a packet to the queue
+ InPacket pkt;
+ pkt.pBuf = p;
+ pkt.addr = *addr;
+ pkt.port = port;
+ m_lInPkt.push_back(pkt);
+
+ queueEvent(NETUDPSOCKET_READABLE);
+}
+
+void LwipNetUdpSocket::cleanUp() //Flush input buffer
+{
+ //Ensure that further error won't be followed to this inst (which can be destroyed)
+ if( m_pPcb )
+ {
+ udp_recv( (udp_pcb*) m_pPcb, NULL, (void*) NULL );
+ }
+
+ //Leaving multicast group(Ok because LwIP has a refscount for multicast group)
+ #if LWIP_IGMP //Multicast support enabled
+ if(m_multicastGroup.isMulticast())
+ {
+ igmp_leavegroup(IP_ADDR_ANY, &(m_multicastGroup.getStruct()));
+ m_multicastGroup = IpAddr();
+ }
+ #endif
+
+ list<InPacket>::iterator it;
+ for ( it = m_lInPkt.begin(); it != m_lInPkt.end(); it++ )
+ {
+ //Free buf
+ pbuf_free((pbuf*)((*it).pBuf));
+ }
+ m_lInPkt.clear();
+}
+
+// Static callback from LwIp
+
+void LwipNetUdpSocket::sRecvCb(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+{
+ LwipNetUdpSocket* pMe = (LwipNetUdpSocket*) arg;
+ return pMe->recvCb( pcb, p, addr, port );
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/lwip/lwipNetUdpSocket.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,84 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef LWIPNETUDPSOCKET_H
+#define LWIPNETUDPSOCKET_H
+
+#define NET_LWIP_STACK 1
+//#include "lwip/ip_addr.h"
+#include "if/net/netudpsocket.h"
+#include "LwipNetIf.h"
+
+#include "stdint.h"
+
+#include <list>
+using std::list;
+
+//Implements NetUdpSockets over lwIP raw API
+
+struct udp_pcb; //Represents a Udp Connection, "Protocol Control Block", see rawapi.txt & udp.h
+struct pbuf; //Lwip Buffer Container
+typedef struct ip_addr ip_addr_t;
+
+//typedef signed char err_t;
+typedef uint16_t u16_t;
+
+class LwipNetUdpSocket: public NetUdpSocket
+{
+public:
+ LwipNetUdpSocket(udp_pcb* pPcb = NULL); //Passes a pcb if already created (by an accept req for instance), in that case transfers ownership
+ virtual ~LwipNetUdpSocket();
+
+ virtual NetUdpSocketErr bind(const Host& me);
+
+ virtual int /*if < 0 : NetUdpSocketErr*/ sendto(const char* buf, int len, Host* pHost);
+ virtual int /*if < 0 : NetUdpSocketErr*/ recvfrom(char* buf, int len, Host* pHost);
+
+ virtual NetUdpSocketErr close();
+
+ virtual NetUdpSocketErr poll();
+
+protected:
+ volatile udp_pcb* m_pPcb;
+
+ //Event callback from lwIp
+ void recvCb(udp_pcb* pcb, struct pbuf* p, ip_addr_t* addr, u16_t port);
+
+private:
+ void cleanUp(); //Flush input buffer
+ struct InPacket
+ {
+ volatile pbuf* pBuf;
+ ip_addr_t addr;
+ u16_t port;
+ };
+
+ list<InPacket> m_lInPkt;
+ IpAddr m_multicastGroup;
+
+ //Static callback : Transforms into a C++ callback
+ static void sRecvCb(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/net/netdnsrequest.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,66 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "netdnsrequest.h"
+#include <string.h>
+
+NetDnsRequest::NetDnsRequest(const char* hostname) : NetService(), m_ip(0,0,0,0), m_pCbItem(NULL), m_pCbMeth(NULL), m_pHost(NULL), m_closed(false)
+{
+ m_hostname = new char[strlen(hostname)+1];
+ strcpy( m_hostname, hostname );
+}
+
+NetDnsRequest::NetDnsRequest(Host* pHost) : NetService(), m_ip(0,0,0,0), m_pCbItem(NULL), m_pCbMeth(NULL), m_closed(false)
+{
+ m_hostname = (char*) pHost->getName();
+ m_pHost = pHost;
+}
+
+NetDnsRequest::~NetDnsRequest()
+{
+ close();
+}
+
+void NetDnsRequest::getResult(IpAddr* pIp)
+{
+ if( pIp != NULL )
+ *pIp = m_ip;
+}
+
+void NetDnsRequest::close()
+{
+ if(m_closed)
+ return;
+ m_closed = true;
+ if( !m_pHost )
+ delete[] m_hostname;
+ NetService::close();
+}
+
+void NetDnsRequest::onReply(NetDnsReply reply) //Must be called by impl when the request completes
+{
+ if( m_pHost )
+ m_pHost->setIp(m_ip);
+ if(m_pCbItem && m_pCbMeth)
+ (m_pCbItem->*m_pCbMeth)(reply);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/net/netdnsrequest.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,84 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef NETDNSREQUEST_H
+#define NETDNSREQUEST_H
+
+//class Socket;
+class Host;
+//class NetService;
+class NetDnsRequest;
+
+#include "netservice.h"
+#include "ipaddr.h"
+#include "host.h"
+
+enum NetDnsReply
+{
+ NETDNS_PRTCL,
+ NETDNS_NOTFOUND, //Hostname is unknown
+ NETDNS_ERROR, //Problem with DNS Service
+ //...
+ NETDNS_FOUND,
+};
+
+class NetDnsRequest : public NetService
+{
+public:
+ NetDnsRequest(const char* hostname);
+ NetDnsRequest(Host* pHost);
+ virtual ~NetDnsRequest();
+
+ class CDummy;
+ template<class T>
+ //Linker bug : Must be defined here :(
+ void setOnReply( T* pItem, void (T::*pMethod)(NetDnsReply) )
+ {
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(NetDnsReply)) pMethod;
+ }
+
+ //Execute request & return OK if found, NOTFOUND or ERROR on error, or PROCESSING if the request has not completed yet
+ // virtual DnsErr pollState() = 0;
+ virtual void poll() = 0; //NetService fn
+
+ void getResult(IpAddr* pIp);
+
+ virtual void close();
+
+protected:
+ void onReply(NetDnsReply reply); //Must be called by impl when the request completes
+
+ IpAddr m_ip;
+ char* m_hostname;
+
+private:
+ CDummy* m_pCbItem;
+ void (CDummy::*m_pCbMeth)(NetDnsReply);
+
+ Host* m_pHost;
+
+ bool m_closed;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/net/netif.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,43 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "core/host.h"
+#include "netif.h"
+#include "core/net.h"
+
+NetIf::NetIf() : m_ip(0,0,0,0)
+{
+ Net::registerIf(this);
+ //Assuming that we only have one if
+ Net::setDefaultIf(this);
+}
+
+NetIf::~NetIf()
+{
+ Net::unregisterIf(this);
+}
+
+IpAddr NetIf::getIp() const
+{
+ return m_ip;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/net/netif.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,64 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef NETIF_H
+#define NETIF_H
+
+#include "core/ipaddr.h"
+/*
+#include "nettcpsocket.h"
+#include "netudpsocket.h"
+#include "netdnsrequest.h"
+*/
+class NetTcpSocket;
+class NetUdpSocket;
+class NetDnsRequest;
+
+#if 0
+enum NetifEvent
+{
+ NETIF_CONNECTED, //Connected, can create & use sockets now
+ NETIF_DNSREPLY,
+ NETIF_DISCONNECTED
+};
+#endif
+
+class NetIf
+{
+public:
+ NetIf();
+ virtual ~NetIf();
+ virtual NetTcpSocket* tcpSocket() = 0; //Create a new tcp socket
+ virtual NetUdpSocket* udpSocket() = 0; //Create a new udp socket
+ virtual void poll() = 0;
+ virtual NetDnsRequest* dnsRequest(const char* hostname) = 0; //Create a new NetDnsRequest object
+ virtual NetDnsRequest* dnsRequest(Host* pHost) = 0; //Create a new NetDnsRequest object
+
+ //!Returns the IP of the interface once it's connected
+ IpAddr getIp() const;
+
+protected:
+ IpAddr m_ip;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/net/nettcpsocket.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,82 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "nettcpsocket.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+NetTcpSocket::NetTcpSocket() : m_host(), m_client(), m_refs(0), m_closed(false), m_removed(false), m_pCbItem(NULL), m_pCbMeth(NULL), m_events()
+{
+ Net::registerNetTcpSocket(this);
+}
+
+NetTcpSocket::~NetTcpSocket() //close()
+{
+ Net::unregisterNetTcpSocket(this);
+}
+
+
+//Callbacks
+#ifdef __LINKER_BUG_SOLVED__
+void NetTcpSocket::setOnEvent()
+{
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(NetTcpSocketEvent)) pMethod;
+}
+#endif
+
+void NetTcpSocket::resetOnEvent()
+{
+ m_pCbItem = NULL;
+ m_pCbMeth = NULL;
+}
+
+void NetTcpSocket::queueEvent(NetTcpSocketEvent e)
+{
+ m_events.push(e);
+}
+
+void NetTcpSocket::discardEvents()
+{
+ while( !m_events.empty() )
+ {
+ m_events.pop();
+ }
+}
+
+void NetTcpSocket::flushEvents() //To be called during polling
+{
+ while( !m_events.empty() )
+ {
+ onEvent(m_events.front());
+ m_events.pop();
+ }
+}
+
+void NetTcpSocket::onEvent(NetTcpSocketEvent e) //To be called during polling
+{
+ if(m_pCbItem && m_pCbMeth)
+ (m_pCbItem->*m_pCbMeth)(e);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/net/nettcpsocket.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,118 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef NETTCPSOCKET_H
+#define NETTCPSOCKET_H
+
+#include "net.h"
+#include "host.h"
+
+#include <queue>
+using std::queue;
+
+//Implements a Berkeley-like socket if
+//Can be interfaced either to lwip or a Telit module
+
+enum NetTcpSocketErr
+{
+ __NETTCPSOCKET_MIN = -0xFFFF,
+ NETTCPSOCKET_SETUP, //NetTcpSocket not properly configured
+ NETTCPSOCKET_TIMEOUT,
+ NETTCPSOCKET_IF, //If has problems
+ NETTCPSOCKET_MEM, //Not enough mem
+ NETTCPSOCKET_INUSE, //If/Port is in use
+ NETTCPSOCKET_EMPTY, //Connections queue is empty
+ NETTCPSOCKET_RST, // Connection was reset by remote host
+//...
+ NETTCPSOCKET_OK = 0
+};
+
+enum NetTcpSocketEvent
+{
+ NETTCPSOCKET_CONNECTED, //Connected to host, must call accept() if we were listening
+ NETTCPSOCKET_ACCEPT, //Connected to client
+ NETTCPSOCKET_READABLE, //Data in buf
+ NETTCPSOCKET_WRITEABLE, //Can write data to buf
+ NETTCPSOCKET_CONTIMEOUT,
+ NETTCPSOCKET_CONRST,
+ NETTCPSOCKET_CONABRT,
+ NETTCPSOCKET_ERROR,
+ NETTCPSOCKET_DISCONNECTED
+};
+
+
+class NetTcpSocket
+{
+public:
+ NetTcpSocket();
+ virtual ~NetTcpSocket(); //close()
+
+ virtual NetTcpSocketErr bind(const Host& me) = 0;
+ virtual NetTcpSocketErr listen() = 0;
+ virtual NetTcpSocketErr connect(const Host& host) = 0;
+ virtual NetTcpSocketErr accept(Host* pClient, NetTcpSocket** ppNewNetTcpSocket) = 0;
+
+ virtual int /*if < 0 : NetTcpSocketErr*/ send(const char* buf, int len) = 0;
+ virtual int /*if < 0 : NetTcpSocketErr*/ recv(char* buf, int len) = 0;
+
+ virtual NetTcpSocketErr close() = 0;
+
+ virtual NetTcpSocketErr poll() = 0;
+
+ class CDummy;
+ //Callbacks
+ template<class T>
+ //Linker bug : Must be defined here :(
+ void setOnEvent( T* pItem, void (T::*pMethod)(NetTcpSocketEvent) )
+ {
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(NetTcpSocketEvent)) pMethod;
+ }
+
+ void resetOnEvent(); //Disable callback
+
+protected:
+ void queueEvent(NetTcpSocketEvent e);
+ void discardEvents();
+ void flushEvents(); //to be called during polling
+
+ Host m_host;
+ Host m_client;
+
+ friend class Net;
+ int m_refs;
+
+ bool m_closed;
+ bool m_removed;
+
+private:
+ //We do not want to execute user code in interrupt routines, so we queue events until the server is polled
+ //If we port this to a multithreaded OS, we could avoid this (however some functions here are not thread-safe, so beware ;) )
+ void onEvent(NetTcpSocketEvent e); //To be called on poll
+ CDummy* m_pCbItem;
+ void (CDummy::*m_pCbMeth)(NetTcpSocketEvent);
+ queue<NetTcpSocketEvent> m_events;
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/net/netudpsocket.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,82 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "netudpsocket.h"
+
+//#define __DEBUG
+#include "dbg/dbg.h"
+
+NetUdpSocket::NetUdpSocket() : m_host(), m_client(), m_refs(0), m_closed(false), m_removed(false), m_pCbItem(NULL), m_pCbMeth(NULL), m_events()
+{
+ Net::registerNetUdpSocket(this);
+}
+
+NetUdpSocket::~NetUdpSocket() //close()
+{
+ Net::unregisterNetUdpSocket(this);
+}
+
+
+//Callbacks
+#if 0 //Just for info
+void NetUdpSocket::setOnEvent()
+{
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(NetUdpSocketEvent)) pMethod;
+}
+#endif
+
+void NetUdpSocket::resetOnEvent()
+{
+ m_pCbItem = NULL;
+ m_pCbMeth = NULL;
+}
+
+void NetUdpSocket::queueEvent(NetUdpSocketEvent e)
+{
+ m_events.push(e);
+}
+
+void NetUdpSocket::discardEvents()
+{
+ while( !m_events.empty() )
+ {
+ m_events.pop();
+ }
+}
+
+void NetUdpSocket::flushEvents() //To be called during polling
+{
+ while( !m_events.empty() )
+ {
+ onEvent(m_events.front());
+ m_events.pop();
+ }
+}
+
+void NetUdpSocket::onEvent(NetUdpSocketEvent e) //To be called during polling
+{
+ if(m_pCbItem && m_pCbMeth)
+ (m_pCbItem->*m_pCbMeth)(e);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/net/netudpsocket.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,106 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#ifndef NETUDPSOCKET_H
+#define NETUDPSOCKET_H
+
+#include "net.h"
+#include "host.h"
+
+#include <queue>
+using std::queue;
+
+//Implements a Berkeley-like socket if
+//Can be interfaced either to lwip or a Telit module
+
+enum NetUdpSocketErr
+{
+ __NETUDPSOCKET_MIN = -0xFFFF,
+ NETUDPSOCKET_SETUP, //NetUdpSocket not properly configured
+ NETUDPSOCKET_IF, //If has problems
+ NETUDPSOCKET_MEM, //Not enough mem
+ NETUDPSOCKET_INUSE, //If/Port is in use
+//...
+ NETUDPSOCKET_OK = 0
+};
+
+enum NetUdpSocketEvent //Only one lonely event here... but who knows, maybe some day there'll be another one!
+{
+ NETUDPSOCKET_READABLE, //Data in buf
+};
+
+
+class NetUdpSocket
+{
+public:
+ NetUdpSocket();
+ virtual ~NetUdpSocket(); //close()
+
+ virtual NetUdpSocketErr bind(const Host& me) = 0;
+
+ virtual int /*if < 0 : NetUdpSocketErr*/ sendto(const char* buf, int len, Host* pHost) = 0;
+ virtual int /*if < 0 : NetUdpSocketErr*/ recvfrom(char* buf, int len, Host* pHost) = 0;
+
+ /* TODO NTH : printf / scanf helpers that call send/recv */
+
+ virtual NetUdpSocketErr close() = 0;
+
+ virtual NetUdpSocketErr poll() = 0;
+
+ class CDummy;
+ //Callbacks
+ template<class T>
+ //Linker bug : Must be defined here :(
+ void setOnEvent( T* pItem, void (T::*pMethod)(NetUdpSocketEvent) )
+ {
+ m_pCbItem = (CDummy*) pItem;
+ m_pCbMeth = (void (CDummy::*)(NetUdpSocketEvent)) pMethod;
+ }
+
+ void resetOnEvent(); //Disable callback
+
+protected:
+ void queueEvent(NetUdpSocketEvent e);
+ void discardEvents();
+ void flushEvents(); //to be called during polling
+
+ Host m_host;
+ Host m_client;
+
+ friend class Net;
+ int m_refs;
+
+ bool m_closed;
+ bool m_removed;
+
+private:
+ //We do not want to execute user code in interrupt routines, so we queue events until the server is polled
+ //If we port this to a multithreaded OS, we could avoid this (however some functions here are not thread-safe, so beware ;) )
+ void onEvent(NetUdpSocketEvent e); //To be called on poll
+ CDummy* m_pCbItem;
+ void (CDummy::*m_pCbMeth)(NetUdpSocketEvent);
+ queue<NetUdpSocketEvent> m_events;
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/ppp/PPPNetIf.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,224 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "PPPNetIf.h"
+#include "mbed.h"
+#include "ppp/ppp.h"
+#include "lwip/init.h"
+#include "lwip/sio.h"
+
+#define __DEBUG
+#include "dbg/dbg.h"
+
+#include "netCfg.h"
+#if NET_PPP
+
+#define PPP_TIMEOUT 60000
+
+#define BUF_SIZE 256
+
+PPPNetIf::PPPNetIf(GPRSModem* pIf) : LwipNetIf(), m_pIf(pIf),/* m_open(false),*/ m_connected(false), m_status(PPP_DISCONNECTED), m_fd(0) //, m_id(0)
+{
+ //FIXME: Check static refcount
+ m_buf = new uint8_t[BUF_SIZE];
+}
+
+PPPNetIf::~PPPNetIf()
+{
+ delete[] m_buf;
+}
+
+#if 0
+PPPErr PPPNetIf::open(Serial* pSerial)
+{
+ GPRSErr err = m_pIf->open(pSerial);
+ if(err)
+ return PPP_MODEM;
+ m_open = true;
+ #if 0
+ m_id = sioMgr::registerSerialIf(this);
+ if(!m_id)
+ {
+ close();
+ return PPP_CLOSED;
+ }
+ #endif
+ return PPP_OK;
+}
+#endif
+
+
+PPPErr PPPNetIf::GPRSConnect(const char* apn, const char* userId, const char* password) //Connect using GPRS
+{
+ LwipNetIf::init();
+ pppInit();
+ //TODO: Tell ATIf that we get ownership of the serial port
+
+ GPRSErr gprsErr;
+ gprsErr = m_pIf->connect(apn);
+ if(gprsErr)
+ return PPP_NETWORK;
+
+ DBG("PPPNetIf: If Connected.\n");
+
+ if( userId == NULL )
+ pppSetAuth(PPPAUTHTYPE_NONE, NULL, NULL);
+ else
+ pppSetAuth(PPPAUTHTYPE_PAP, userId, password); //TODO: Allow CHAP as well
+
+ DBG("PPPNetIf: Set Auth.\n");
+
+ //wait(1.);
+
+ //m_pIf->flushBuffer(); //Flush buffer before passing serial port to PPP
+
+ m_status = PPP_CONNECTING;
+ DBG("m_pIf = %p\n", m_pIf);
+ int res = pppOverSerialOpen((void*)m_pIf, sPppCallback, (void*)this);
+ DBG("PPP connected\n");
+ if(res<0)
+ {
+ disconnect();
+ return PPP_PROTOCOL;
+ }
+
+ DBG("PPPNetIf: PPP Started with res = %d.\n", res);
+
+ m_fd = res;
+ m_connected = true;
+ Timer t;
+ t.start();
+ while( m_status == PPP_CONNECTING ) //Wait for callback
+ {
+ poll();
+ if(t.read_ms()>PPP_TIMEOUT)
+ {
+ DBG("PPPNetIf: Timeout.\n");
+ disconnect();
+ return PPP_PROTOCOL;
+ }
+ }
+
+ DBG("PPPNetIf: Callback returned.\n");
+
+ if( m_status == PPP_DISCONNECTED )
+ {
+ disconnect();
+ return PPP_PROTOCOL;
+ }
+
+ return PPP_OK;
+
+}
+
+PPPErr PPPNetIf::ATConnect(const char* number) //Connect using a "classic" voice modem or GSM
+{
+ //TODO: IMPL
+ return PPP_MODEM;
+}
+
+PPPErr PPPNetIf::disconnect()
+{
+ if(m_fd)
+ pppClose(m_fd); //0 if ok, else should gen a WARN
+ m_connected = false;
+
+ m_pIf->flushBuffer();
+ m_pIf->printf("+++\r\n");
+ wait(.5);
+ m_pIf->flushBuffer();
+
+ GPRSErr gprsErr;
+ gprsErr = m_pIf->disconnect();
+ if(gprsErr)
+ return PPP_NETWORK;
+
+ return PPP_OK;
+}
+
+#if 0
+PPPErr PPPNetIf::close()
+{
+ GPRSErr err = m_pIf->close();
+ if(err)
+ return PPP_MODEM;
+ m_open = false;
+ return PPP_OK;
+}
+#endif
+
+
+#if 0
+//We have to use :
+
+/** Pass received raw characters to PPPoS to be decoded. This function is
+ * thread-safe and can be called from a dedicated RX-thread or from a main-loop.
+ *
+ * @param pd PPP descriptor index, returned by pppOpen()
+ * @param data received data
+ * @param len length of received data
+ */
+void
+pppos_input(int pd, u_char* data, int len)
+{
+ pppInProc(&pppControl[pd].rx, data, len);
+}
+#endif
+
+void PPPNetIf::poll()
+{
+ if(!m_connected)
+ return;
+ LwipNetIf::poll();
+ //static u8_t buf[128];
+ int len;
+ do
+ {
+ len = sio_tryread((sio_fd_t) m_pIf, m_buf, BUF_SIZE);
+ if(len > 0)
+ pppos_input(m_fd, m_buf, len);
+ } while(len>0);
+}
+
+//Link Callback
+void PPPNetIf::pppCallback(int errCode, void *arg)
+{
+ switch ( errCode )
+ {
+ //No error
+ case PPPERR_NONE:
+ {
+ struct ppp_addrs* addrs = (struct ppp_addrs*) arg;
+ m_ip = IpAddr(&(addrs->our_ipaddr)); //Set IP
+ }
+ m_status = PPP_CONNECTED;
+ break;
+ default:
+ //Disconnected
+ DBG("PPPNetIf: Callback errCode = %d.\n", errCode);
+ m_status = PPP_DISCONNECTED;
+ break;
+ }
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/ppp/PPPNetIf.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,100 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+/** \file
+PPP Generic network interface header file
+*/
+
+//This is a backend for PPP, using lwIP
+
+#ifndef PPPNETIF_H
+#define PPPNETIF_H
+
+#include "mbed.h"
+
+//For now, only a GPRS Modem is supported,
+//but we could easily split this class into a base class + two derived classes
+// (PPP over GPRS + PPP over Serial Modem)
+#include "drv/gprs/GPRSModem.h"
+#include "if/lwip/LwipNetIf.h"
+
+///PPP connection error codes
+enum PPPErr
+{
+ __PPP_MIN = -0xFFFF,
+ PPP_MODEM, ///<AT error returned
+ PPP_NETWORK, ///<Network is down
+ PPP_PROTOCOL, ///<PPP Protocol error
+ PPP_CLOSED, ///<Connection is closed
+ PPP_OK = 0 ///<Success
+};
+
+enum PPPStatus
+{
+ PPP_CONNECTING,
+ PPP_CONNECTED,
+ PPP_DISCONNECTED,
+};
+
+class PPPNetIf : public LwipNetIf
+{
+public:
+ PPPNetIf(GPRSModem* pIf);
+ virtual ~PPPNetIf();
+
+ #if 0
+ PPPErr open(Serial* pSerial);
+ #endif
+
+ PPPErr GPRSConnect(const char* apn, const char* userId, const char* password); //Connect using GPRS
+ PPPErr ATConnect(const char* number); //Connect using a "classic" voice modem or GSM
+
+ virtual void poll();
+
+ PPPErr disconnect();
+
+ #if 0
+ PPPErr close();
+ #endif
+
+protected:
+ GPRSModem* m_pIf;
+
+private:
+ void pppCallback(int errCode, void *arg);
+
+ static void sPppCallback(void *ctx, int errCode, void *arg) //Callback from ppp.c
+ { return ((PPPNetIf*)ctx)->pppCallback(errCode, arg); }
+
+ bool m_connected;
+ /*bool m_open;*/
+ volatile PPPStatus m_status;
+ volatile int m_fd; //PPP Session descriptor
+
+ //int m_id;
+
+ uint8_t* m_buf;
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/umtsstick/UMTSStickNetIf.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,92 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+#include "netCfg.h"
+#if NET_UMTS
+
+#include "UMTSStickNetIf.h"
+
+#define __DEBUG
+#include "dbg/dbg.h"
+
+UMTSStickNetIf::UMTSStickNetIf() : PPPNetIf(NULL), m_umtsStick(), m_pUsbSerial(NULL)
+{
+ PPPNetIf::m_pIf = new GPRSModem();
+}
+
+UMTSStickNetIf::~UMTSStickNetIf()
+{
+ delete PPPNetIf::m_pIf;
+ if(m_pUsbSerial)
+ delete m_pUsbSerial;
+}
+
+UMTSStickErr UMTSStickNetIf::setup() //UMTSStickErr is from /drv/umtsstick/UMTSStick.h
+{
+ return m_umtsStick.getSerial(&m_pUsbSerial);
+}
+
+PPPErr UMTSStickNetIf::connect(const char* apn /*= NULL*/, const char* userId /*= NULL*/, const char* password /*= NULL*/) //Connect using GPRS
+{
+ if(!m_pUsbSerial)
+ return PPP_MODEM;
+
+ ATErr atErr;
+ for(int i=0; i<3; i++)
+ {
+ atErr = m_pIf->open(m_pUsbSerial); //3 tries
+ if(!atErr)
+ break;
+ wait(4);
+ }
+
+ if(atErr)
+ return PPP_MODEM;
+
+ PPPErr pppErr;
+ for(int i=0; i<3; i++)
+ {
+ pppErr = PPPNetIf::GPRSConnect(apn, userId, password);
+ if(!pppErr)
+ break;
+ wait(4);
+ }
+ if(pppErr)
+ return pppErr;
+
+ return PPP_OK;
+}
+
+PPPErr UMTSStickNetIf::disconnect()
+{
+ PPPErr pppErr = PPPNetIf::disconnect();
+ if(pppErr)
+ return pppErr;
+
+ m_pIf->close();
+
+ return PPP_OK;
+}
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/if/umtsstick/UMTSStickNetIf.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,80 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+
+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.
+*/
+
+/** \file
+UMTS Stick network interface header file
+*/
+
+#ifndef UMTSSTICKNETIF_H
+#define UMTSSTICKNETIF_H
+
+#include "mbed.h"
+
+#include "core/net.h"
+#include "if/ppp/PPPNetIf.h"
+
+#include "drv/umtsstick/UMTSStick.h"
+
+///UMTS Stick network interface
+/**
+This class provides connectivity to the stack using a 3G (or LTE etc...) stick
+Plug it to your USB host using two Pull-down resistors on the D+/D- lines
+*/
+class UMTSStickNetIf : public LwipNetIf, protected PPPNetIf
+{
+public:
+ ///Instantiates the Interface and register it against the stack
+ UMTSStickNetIf();
+ virtual ~UMTSStickNetIf();
+
+ ///Tries to connect to the stick
+ /**
+ This method tries to obtain a virtual serial port interface from the stick
+ It waits for a stick to be connected, switches it from CDFS to virtual serial port mode if needed,
+ and obtains a virtual serial port from it
+ @return : A negative error code on error or 0 on success
+ */
+ UMTSStickErr setup(); //UMTSStickErr is from /drv/umtsstick/UMTSStick.h
+
+ ///Establishes a PPP connection
+ /**
+ This method opens an AT interface on the serial interface, initializes and configures the stick,
+ then opens a PPP connection and authenticates with the parameters
+ \param apn : APN of the interface, if NULL uses the SIM default value
+ \param userId : user with which to authenticate during the PPP connection, if NULL does not authenticate
+ \param password : associated password
+ @return : A negative error code on error or 0 on success
+ */
+ PPPErr connect(const char* apn = NULL, const char* userId = NULL, const char* password = NULL); //Connect using GPRS
+
+ ///Disconnects the PPP connection
+ PPPErr disconnect();
+
+private:
+ UMTSStick m_umtsStick;
+ UsbSerial* m_pUsbSerial;
+
+};
+
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwip/arch/cc.h Thu Sep 08 10:48:09 2011 +0000 @@ -0,0 +1,62 @@ +/* + * Author: Adam Dunkels <adam@sics.se> + * + */ +#ifndef __LWIP_ARCH_CC_H__ +#define __LWIP_ARCH_CC_H__ + +#define LITTLE_ENDIAN 1234 + +#define BYTE_ORDER LITTLE_ENDIAN + +typedef unsigned char u8_t; +typedef signed char s8_t; +typedef unsigned short u16_t; +typedef signed short s16_t; +typedef unsigned int u32_t; +typedef signed int s32_t; +typedef unsigned int mem_ptr_t; + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#include <stdlib.h> +#define LWIP_RAND rand + +#define LWIP_PLATFORM_DIAG(x) DBG x +#define LWIP_PLATFORM_ASSERT(x) DBG(x) + +#define LWIP_PROVIDE_ERRNO + +#define U16_F "hu" +#define S16_F "hd" +#define X16_F "hx" +#define U32_F "lu" +#define S32_F "ld" +#define X32_F "lx" + +#if 0 +/*Create compilation problems, and according to http://www.mail-archive.com/lwip-users@nongnu.org/msg06786.html, +lwIP uses packed structures, so packing the field is not really a good idea ;) */ +#define PACK_STRUCT_FIELD(x) __packed x +#else +#define PACK_STRUCT_FIELD(x) x +#endif + +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_END + +#define LWIP_CHKSUM_ALGORITHM 3 + + +#endif /* __LWIP_ARCH_CC_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/arch/perf.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,23 @@
+/*
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_ARCH_PERF_H__
+#define __LWIP_ARCH_PERF_H__
+
+#define PERF_START
+#define PERF_STOP(x)
+
+#define perf_init(fname)
+
+#if 0
+#ifdef __cplusplus
+inline
+#endif
+void perf_init(char *fname) {
+ return;
+}
+#endif
+
+
+#endif /* __LWIP_ARCH_PERF_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/arch/sys_arch.cpp Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,70 @@
+#include "sys_arch.h"
+#include "mbed.h"
+//DG 2010
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __COMPLEX_AND_BUGGUY_HANDLING__
+static Timer* pTmr = NULL;
+
+void sys_init(void)
+{
+ //Start Timer
+ pTmr = new Timer();
+ pTmr->start();
+}
+
+u32_t sys_jiffies(void) /* since power up. */
+{
+ return (u32_t) (pTmr->read_ms()/10); //In /10ms units
+}
+
+u32_t sys_now(void)
+{
+ return (u32_t) pTmr->read_ms(); //In /ms units
+}
+#elif0
+void sys_init(void)
+{
+
+}
+
+u32_t sys_jiffies(void) /* since power up. */
+{
+ static int count = 0;
+ return ++count;
+}
+
+u32_t sys_now(void)
+{
+ return (u32_t) time(NULL);
+}
+#else
+static Timer* pTmr = NULL;
+
+void sys_init(void)
+{
+ //Start Timer
+ pTmr = new Timer();
+ pTmr->start();
+}
+
+u32_t sys_jiffies(void) /* since power up. */
+{
+ static int count = 0;
+ return ++count;
+ //return (u32_t) (pTmr->read_us());
+}
+
+u32_t sys_now(void)
+{
+ return (u32_t) (pTmr->read_ms()); //In /ms units
+}
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/arch/sys_arch.h Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,27 @@
+/*
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_SYS_ARCH_H__
+#define __LWIP_SYS_ARCH_H__
+
+typedef unsigned int u32_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//DG 2010
+void sys_init(void); /* To be called first */
+u32_t sys_jiffies(void); /* since power up. */
+
+/** Returns the current time in milliseconds,
+ * may be the same as sys_jiffies or at least based on it. */
+u32_t sys_now(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __LWIP_ARCH_CC_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/def.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,108 @@
+/**
+ * @file
+ * Common functions used throughout the stack.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+/**
+ * These are reference implementations of the byte swapping functions.
+ * Again with the aim of being simple, correct and fully portable.
+ * Byte swapping is the second thing you would want to optimize. You will
+ * need to port it to your architecture and in your cc.h:
+ *
+ * #define LWIP_PLATFORM_BYTESWAP 1
+ * #define LWIP_PLATFORM_HTONS(x) <your_htons>
+ * #define LWIP_PLATFORM_HTONL(x) <your_htonl>
+ *
+ * Note ntohs() and ntohl() are merely references to the htonx counterparts.
+ */
+
+#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN)
+
+/**
+ * Convert an u16_t from host- to network byte order.
+ *
+ * @param n u16_t in host byte order
+ * @return n in network byte order
+ */
+u16_t
+lwip_htons(u16_t n)
+{
+ return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
+}
+
+/**
+ * Convert an u16_t from network- to host byte order.
+ *
+ * @param n u16_t in network byte order
+ * @return n in host byte order
+ */
+u16_t
+lwip_ntohs(u16_t n)
+{
+ return lwip_htons(n);
+}
+
+/**
+ * Convert an u32_t from host- to network byte order.
+ *
+ * @param n u32_t in host byte order
+ * @return n in network byte order
+ */
+u32_t
+lwip_htonl(u32_t n)
+{
+ return ((n & 0xff) << 24) |
+ ((n & 0xff00) << 8) |
+ ((n & 0xff0000UL) >> 8) |
+ ((n & 0xff000000UL) >> 24);
+}
+
+/**
+ * Convert an u32_t from network- to host byte order.
+ *
+ * @param n u32_t in network byte order
+ * @return n in host byte order
+ */
+u32_t
+lwip_ntohl(u32_t n)
+{
+ return lwip_htonl(n);
+}
+
+#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/dhcp.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,1725 @@
+/**
+ * @file
+ * Dynamic Host Configuration Protocol client
+ *
+ */
+
+/*
+ *
+ * Copyright (c) 2001-2004 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 2131 and RFC 2132.
+ *
+ * TODO:
+ * - Support for interfaces other than Ethernet (SLIP, PPP, ...)
+ *
+ * Please coordinate changes and requests with Leon Woestenberg
+ * <leon.woestenberg@gmx.net>
+ *
+ * Integration with your code:
+ *
+ * In lwip/dhcp.h
+ * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute)
+ * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer)
+ *
+ * Then have your application call dhcp_coarse_tmr() and
+ * dhcp_fine_tmr() on the defined intervals.
+ *
+ * dhcp_start(struct netif *netif);
+ * starts a DHCP client instance which configures the interface by
+ * obtaining an IP address lease and maintaining it.
+ *
+ * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif)
+ * to remove the DHCP client.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/dns.h"
+#include "netif/etharp.h"
+
+#include <string.h>
+
+/** Default for DHCP_GLOBAL_XID is 0xABCD0000
+ * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g.
+ * #define DHCP_GLOBAL_XID_HEADER "stdlib.h"
+ * #define DHCP_GLOBAL_XID rand()
+ */
+#ifdef DHCP_GLOBAL_XID_HEADER
+#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */
+#endif
+
+/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU
+ * MTU is checked to be big enough in dhcp_start */
+#define DHCP_MAX_MSG_LEN(netif) (netif->mtu)
+#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576
+/** Minimum length for reply before packet is parsed */
+#define DHCP_MIN_REPLY_LEN 44
+
+#define REBOOT_TRIES 2
+
+/** Option handling: options are parsed in dhcp_parse_reply
+ * and saved in an array where other functions can load them from.
+ * This might be moved into the struct dhcp (not necessarily since
+ * lwIP is single-threaded and the array is only used while in recv
+ * callback). */
+#define DHCP_OPTION_IDX_OVERLOAD 0
+#define DHCP_OPTION_IDX_MSG_TYPE 1
+#define DHCP_OPTION_IDX_SERVER_ID 2
+#define DHCP_OPTION_IDX_LEASE_TIME 3
+#define DHCP_OPTION_IDX_T1 4
+#define DHCP_OPTION_IDX_T2 5
+#define DHCP_OPTION_IDX_SUBNET_MASK 6
+#define DHCP_OPTION_IDX_ROUTER 7
+#define DHCP_OPTION_IDX_DNS_SERVER 8
+#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
+
+/** Holds the decoded option values, only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
+/** Holds a flag which option was received and is contained in dhcp_rx_options_val,
+ only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX];
+
+#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0)
+#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1)
+#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0)
+#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given)))
+#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx])
+#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val))
+
+
+/* DHCP client state machine functions */
+static err_t dhcp_discover(struct netif *netif);
+static err_t dhcp_select(struct netif *netif);
+static void dhcp_bind(struct netif *netif);
+#if DHCP_DOES_ARP_CHECK
+static err_t dhcp_decline(struct netif *netif);
+#endif /* DHCP_DOES_ARP_CHECK */
+static err_t dhcp_rebind(struct netif *netif);
+static err_t dhcp_reboot(struct netif *netif);
+static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+
+/* set the DHCP timers */
+static void dhcp_timeout(struct netif *netif);
+static void dhcp_t1_timeout(struct netif *netif);
+static void dhcp_t2_timeout(struct netif *netif);
+
+/* build outgoing messages */
+/* create a DHCP message, fill in common headers */
+static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type);
+/* free a DHCP request */
+static void dhcp_delete_msg(struct dhcp *dhcp);
+/* add a DHCP option (type, then length in bytes) */
+static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len);
+/* add option values */
+static void dhcp_option_byte(struct dhcp *dhcp, u8_t value);
+static void dhcp_option_short(struct dhcp *dhcp, u16_t value);
+static void dhcp_option_long(struct dhcp *dhcp, u32_t value);
+/* always add the DHCP options trailer to end and pad */
+static void dhcp_option_trailer(struct dhcp *dhcp);
+
+/**
+ * Back-off the DHCP client (because of a received NAK response).
+ *
+ * Back-off the DHCP client because of a received NAK. Receiving a
+ * NAK means the client asked for something non-sensible, for
+ * example when it tries to renew a lease obtained on another network.
+ *
+ * We clear any existing set IP address and restart DHCP negotiation
+ * afresh (as per RFC2131 3.2.3).
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_nak(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* Set the interface down since the address must no longer be used, as per RFC2131 */
+ netif_set_down(netif);
+ /* remove IP address from interface */
+ netif_set_ipaddr(netif, IP_ADDR_ANY);
+ netif_set_gw(netif, IP_ADDR_ANY);
+ netif_set_netmask(netif, IP_ADDR_ANY);
+ /* Change to a defined state */
+ dhcp_set_state(dhcp, DHCP_BACKING_OFF);
+ /* We can immediately restart discovery */
+ dhcp_discover(netif);
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Checks if the offered IP address is already in use.
+ *
+ * It does so by sending an ARP request for the offered address and
+ * entering CHECKING state. If no ARP reply is received within a small
+ * interval, the address is assumed to be free for use by us.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_check(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
+ (s16_t)netif->name[1]));
+ dhcp_set_state(dhcp, DHCP_CHECKING);
+ /* create an ARP query for the offered IP address, expecting that no host
+ responds, as the IP address should not be in use. */
+ result = etharp_query(netif, &dhcp->offered_ip_addr, NULL);
+ if (result != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n"));
+ }
+ dhcp->tries++;
+ msecs = 500;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+/**
+ * Remember the configuration offered by a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_offer(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* obtain the server address */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
+ ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->server_ip_addr)));
+ /* remember offered address */
+ ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif));
+ }
+}
+
+/**
+ * Select a DHCP server offer out of all offers.
+ *
+ * Simply select the first offer received.
+ *
+ * @param netif the netif under DHCP control
+ * @return lwIP specific error (see error.h)
+ */
+static err_t
+dhcp_select(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ dhcp_set_state(dhcp, DHCP_REQUESTING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ /* MUST request the offered IP address */
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_addr)));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
+ dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
+ dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
+ dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
+ dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
+
+#if LWIP_NETIF_HOSTNAME
+ if (netif->hostname != NULL) {
+ const char *p = (const char*)netif->hostname;
+ u8_t namelen = (u8_t)strlen(p);
+ if (namelen > 0) {
+ LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
+ dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
+ while (*p) {
+ dhcp_option_byte(dhcp, *p++);
+ }
+ }
+ }
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ dhcp_option_trailer(dhcp);
+ /* shrink the pbuf to the actual content length */
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* send broadcast to any DHCP server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * The DHCP timer that checks for lease renewal/rebind timeouts.
+ */
+void
+dhcp_coarse_tmr()
+{
+ struct netif *netif = netif_list;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
+ /* iterate through all network interfaces */
+ while (netif != NULL) {
+ /* only act on DHCP configured interfaces */
+ if (netif->dhcp != NULL) {
+ /* timer is active (non zero), and triggers (zeroes) now? */
+ if (netif->dhcp->t2_timeout-- == 1) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
+ /* this clients' rebind timeout triggered */
+ dhcp_t2_timeout(netif);
+ /* timer is active (non zero), and triggers (zeroes) now */
+ } else if (netif->dhcp->t1_timeout-- == 1) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
+ /* this clients' renewal timeout triggered */
+ dhcp_t1_timeout(netif);
+ }
+ }
+ /* proceed to next netif */
+ netif = netif->next;
+ }
+}
+
+/**
+ * DHCP transaction timeout handling
+ *
+ * A DHCP server is expected to respond within a short period of time.
+ * This timer checks whether an outstanding DHCP request is timed out.
+ */
+void
+dhcp_fine_tmr()
+{
+ struct netif *netif = netif_list;
+ /* loop through netif's */
+ while (netif != NULL) {
+ /* only act on DHCP configured interfaces */
+ if (netif->dhcp != NULL) {
+ /* timer is active (non zero), and is about to trigger now */
+ if (netif->dhcp->request_timeout > 1) {
+ netif->dhcp->request_timeout--;
+ }
+ else if (netif->dhcp->request_timeout == 1) {
+ netif->dhcp->request_timeout--;
+ /* { netif->dhcp->request_timeout == 0 } */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
+ /* this client's request timeout triggered */
+ dhcp_timeout(netif);
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+
+/**
+ * A DHCP negotiation transaction, or ARP request, has timed out.
+ *
+ * The timer that was started with the DHCP or ARP request has
+ * timed out, indicating no response was received in time.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n"));
+ /* back-off period has passed, or server selection timed out */
+ if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n"));
+ dhcp_discover(netif);
+ /* receiving the requested lease timed out */
+ } else if (dhcp->state == DHCP_REQUESTING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
+ if (dhcp->tries <= 5) {
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
+ dhcp_release(netif);
+ dhcp_discover(netif);
+ }
+#if DHCP_DOES_ARP_CHECK
+ /* received no ARP reply for the offered address (which is good) */
+ } else if (dhcp->state == DHCP_CHECKING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n"));
+ if (dhcp->tries <= 1) {
+ dhcp_check(netif);
+ /* no ARP replies on the offered address,
+ looks like the IP address is indeed free */
+ } else {
+ /* bind the interface to the offered address */
+ dhcp_bind(netif);
+ }
+#endif /* DHCP_DOES_ARP_CHECK */
+ }
+ /* did not get response to renew request? */
+ else if (dhcp->state == DHCP_RENEWING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n"));
+ /* just retry renewal */
+ /* note that the rebind timer will eventually time-out if renew does not work */
+ dhcp_renew(netif);
+ /* did not get response to rebind request? */
+ } else if (dhcp->state == DHCP_REBINDING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n"));
+ if (dhcp->tries <= 8) {
+ dhcp_rebind(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n"));
+ dhcp_release(netif);
+ dhcp_discover(netif);
+ }
+ } else if (dhcp->state == DHCP_REBOOTING) {
+ if (dhcp->tries < REBOOT_TRIES) {
+ dhcp_reboot(netif);
+ } else {
+ dhcp_discover(netif);
+ }
+ }
+}
+
+/**
+ * The renewal period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t1_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n"));
+ if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) ||
+ (dhcp->state == DHCP_RENEWING)) {
+ /* just retry to renew - note that the rebind timer (t2) will
+ * eventually time-out if renew tries fail. */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t1_timeout(): must renew\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_RENEWING, not DHCP_BOUND */
+ dhcp_renew(netif);
+ }
+}
+
+/**
+ * The rebind period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t2_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n"));
+ if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) ||
+ (dhcp->state == DHCP_RENEWING)) {
+ /* just retry to rebind */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t2_timeout(): must rebind\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_REBINDING, not DHCP_BOUND */
+ dhcp_rebind(netif);
+ }
+}
+
+/**
+ * Handle a DHCP ACK packet
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_ack(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+#if LWIP_DNS
+ u8_t n;
+#endif /* LWIP_DNS */
+
+ /* clear options we might not get from the ACK */
+ ip_addr_set_zero(&dhcp->offered_sn_mask);
+ ip_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* lease time given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
+ /* remember offered lease time */
+ dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
+ }
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
+ /* remember given renewal period */
+ dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
+ } else {
+ /* calculate safe periods for renewal */
+ dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
+ }
+
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
+ /* remember given rebind period */
+ dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
+ } else {
+ /* calculate safe periods for rebinding */
+ dhcp->offered_t2_rebind = dhcp->offered_t0_lease;
+ }
+
+ /* (y)our internet address */
+ ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+
+#if LWIP_DHCP_BOOTP_FILE
+ /* copy boot server address,
+ boot file name copied in dhcp_parse_reply if not overloaded */
+ ip_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* subnet mask given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
+ /* remember given subnet mask */
+ ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
+ dhcp->subnet_mask_given = 1;
+ } else {
+ dhcp->subnet_mask_given = 0;
+ }
+
+ /* gateway router */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
+ ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
+ }
+
+#if LWIP_DNS
+ /* DNS servers */
+ n = 0;
+ while(dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n) && (n < DNS_MAX_SERVERS)) {
+ ip_addr_t dns_addr;
+ ip4_addr_set_u32(&dns_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
+ dns_setserver(n, &dns_addr);
+ n++;
+ }
+#endif /* LWIP_DNS */
+}
+
+/** Set a statically allocated struct dhcp to work with.
+ * Using this prevents dhcp_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+dhcp_set_struct(struct netif *netif, struct dhcp *dhcp)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("dhcp != NULL", dhcp != NULL);
+ LWIP_ASSERT("netif already has a struct dhcp set", netif->dhcp == NULL);
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_OFF); */
+ netif->dhcp = dhcp;
+}
+
+/**
+ * Start DHCP negotiation for a network interface.
+ *
+ * If no DHCP client instance was attached to this interface,
+ * a new client is created first. If a DHCP client instance
+ * was already present, it restarts negotiation.
+ *
+ * @param netif The lwIP network interface
+ * @return lwIP error code
+ * - ERR_OK - No error
+ * - ERR_MEM - Out of memory
+ */
+err_t
+dhcp_start(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ err_t result = ERR_OK;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
+ dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* Remove the flag that says this netif is handled by DHCP,
+ it is set when we succeeded starting. */
+ netif->flags &= ~NETIF_FLAG_DHCP;
+
+ /* check hwtype of the netif */
+ if ((netif->flags & NETIF_FLAG_ETHARP) == 0) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n"));
+ return ERR_ARG;
+ }
+
+ /* check MTU of the netif */
+ if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
+ return ERR_MEM;
+ }
+
+ /* no DHCP client attached yet? */
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
+ dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
+ return ERR_MEM;
+ }
+ /* store this dhcp client in the netif */
+ netif->dhcp = dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
+ /* already has DHCP client attached */
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
+ if (dhcp->pcb != NULL) {
+ udp_remove(dhcp->pcb);
+ }
+ LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL);
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL );
+ }
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_OFF); */
+ /* allocate UDP PCB */
+ dhcp->pcb = udp_new();
+ if (dhcp->pcb == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n"));
+ return ERR_MEM;
+ }
+ dhcp->pcb->so_options |= SOF_BROADCAST;
+ /* set up local and remote port for the pcb */
+ udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+ udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
+ /* set up the recv callback and argument */
+ udp_recv(dhcp->pcb, dhcp_recv, netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
+ /* (re)start the DHCP negotiation */
+ result = dhcp_discover(netif);
+ if (result != ERR_OK) {
+ /* free resources allocated above */
+ dhcp_stop(netif);
+ return ERR_MEM;
+ }
+ /* Set the flag that says this netif is handled by DHCP. */
+ netif->flags |= NETIF_FLAG_DHCP;
+ return result;
+}
+
+/**
+ * Inform a DHCP server of our manual configuration.
+ *
+ * This informs DHCP servers of our fixed IP address configuration
+ * by sending an INFORM message. It does not involve DHCP address
+ * configuration, it is just here to be nice to the network.
+ *
+ * @param netif The lwIP network interface
+ */
+void
+dhcp_inform(struct netif *netif)
+{
+ struct dhcp dhcp;
+ err_t result = ERR_OK;
+ struct udp_pcb *pcb;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ memset(&dhcp, 0, sizeof(struct dhcp));
+ dhcp_set_state(&dhcp, DHCP_INFORM);
+
+ if ((netif->dhcp != NULL) && (netif->dhcp->pcb != NULL)) {
+ /* re-use existing pcb */
+ pcb = netif->dhcp->pcb;
+ } else {
+ pcb = udp_new();
+ if (pcb == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb"));
+ return;
+ }
+ dhcp.pcb = pcb;
+ dhcp.pcb->so_options |= SOF_BROADCAST;
+ udp_bind(dhcp.pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n"));
+ }
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM);
+ if (result == ERR_OK) {
+ dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option_trailer(&dhcp);
+
+ pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
+ udp_sendto_if(pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(&dhcp);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n"));
+ }
+
+ if (dhcp.pcb != NULL) {
+ /* otherwise, the existing pcb was used */
+ udp_remove(dhcp.pcb);
+ }
+}
+
+/** Handle a possible change in the network configuration.
+ *
+ * This enters the REBOOTING state to verify that the currently bound
+ * address is still valid.
+ */
+void
+dhcp_network_changed(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ if (!dhcp)
+ return;
+ switch (dhcp->state) {
+ case DHCP_REBINDING:
+ case DHCP_RENEWING:
+ case DHCP_BOUND:
+ case DHCP_REBOOTING:
+ netif_set_down(netif);
+ dhcp->tries = 0;
+ dhcp_reboot(netif);
+ break;
+ case DHCP_OFF:
+ /* stay off */
+ break;
+ default:
+ dhcp->tries = 0;
+#if LWIP_DHCP_AUTOIP_COOP
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ dhcp_discover(netif);
+ break;
+ }
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Match an ARP reply with the offered IP address.
+ *
+ * @param netif the network interface on which the reply was received
+ * @param addr The IP address we received a reply from
+ */
+void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr)
+{
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n"));
+ /* is a DHCP client doing an ARP check? */
+ if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(addr)));
+ /* did a host respond with the address we
+ were offered by the DHCP server? */
+ if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) {
+ /* we will not accept the offered address */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("dhcp_arp_reply(): arp reply matched with offered address, declining\n"));
+ dhcp_decline(netif);
+ }
+ }
+}
+
+/**
+ * Decline an offered lease.
+ *
+ * Tell the DHCP server we do not accept the offered address.
+ * One reason to decline the lease is when we find out the address
+ * is already in use by another host (through ARP).
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_decline(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result = ERR_OK;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
+ dhcp_set_state(dhcp, DHCP_BACKING_OFF);
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option_trailer(dhcp);
+ /* resize pbuf to reflect true size of options */
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* per section 4.4.4, broadcast DECLINE messages */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_decline: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = 10*1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+
+/**
+ * Start the DHCP process, discover a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_discover(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result = ERR_OK;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
+ ip_addr_set_any(&dhcp->offered_ip_addr);
+ dhcp_set_state(dhcp, DHCP_SELECTING);
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER);
+ if (result == ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
+
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
+ dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
+ dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
+ dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
+ dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
+
+ dhcp_option_trailer(dhcp);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n"));
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
+ autoip_start(netif);
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * Bind the interface to the offered IP address.
+ *
+ * @param netif network interface to bind to the offered address
+ */
+static void
+dhcp_bind(struct netif *netif)
+{
+ u32_t timeout;
+ struct dhcp *dhcp;
+ ip_addr_t sn_mask, gw_addr;
+ LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;);
+ dhcp = netif->dhcp;
+ LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ /* temporary DHCP lease? */
+ if (dhcp->offered_t1_renew != 0xffffffffUL) {
+ /* set renewal period timer */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
+ timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if(timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t1_timeout = (u16_t)timeout;
+ if (dhcp->t1_timeout == 0) {
+ dhcp->t1_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000));
+ }
+ /* set renewal period timer */
+ if (dhcp->offered_t2_rebind != 0xffffffffUL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
+ timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if(timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t2_timeout = (u16_t)timeout;
+ if (dhcp->t2_timeout == 0) {
+ dhcp->t2_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000));
+ }
+
+ if (dhcp->subnet_mask_given) {
+ /* copy offered network mask */
+ ip_addr_copy(sn_mask, dhcp->offered_sn_mask);
+ } else {
+ /* subnet mask not given, choose a safe subnet mask given the network class */
+ u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
+ if (first_octet <= 127) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000));
+ } else if (first_octet >= 192) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00));
+ } else {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000));
+ }
+ }
+
+ ip_addr_copy(gw_addr, dhcp->offered_gw_addr);
+ /* gateway address not given? */
+ if (ip_addr_isany(&gw_addr)) {
+ /* copy network address */
+ ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
+ /* use first host address on network as gateway */
+ ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001));
+ }
+
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+ netif_set_ipaddr(netif, &dhcp->offered_ip_addr);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&sn_mask)));
+ netif_set_netmask(netif, &sn_mask);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&gw_addr)));
+ netif_set_gw(netif, &gw_addr);
+ /* bring the interface up */
+ netif_set_up(netif);
+ /* netif is now bound to DHCP leased address */
+ dhcp_set_state(dhcp, DHCP_BOUND);
+}
+
+/**
+ * Renew an existing DHCP lease at the involved DHCP server.
+ *
+ * @param netif network interface which must renew its lease
+ */
+err_t
+dhcp_renew(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
+ dhcp_set_state(dhcp, DHCP_RENEWING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+#if LWIP_NETIF_HOSTNAME
+ if (netif->hostname != NULL) {
+ const char *p = (const char*)netif->hostname;
+ u8_t namelen = (u8_t)strlen(p);
+ if (namelen > 0) {
+ LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
+ dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
+ while (*p) {
+ dhcp_option_byte(dhcp, *p++);
+ }
+ }
+ }
+#endif /* LWIP_NETIF_HOSTNAME */
+
+#if 0
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
+#endif
+
+#if 0
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
+#endif
+ /* append DHCP message trailer */
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ /* back-off on retries, but to a maximum of 20 seconds */
+ msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Rebind with a DHCP server for an existing DHCP lease.
+ *
+ * @param netif network interface which must rebind with a DHCP server
+ */
+static err_t
+dhcp_rebind(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n"));
+ dhcp_set_state(dhcp, DHCP_REBINDING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+#if LWIP_NETIF_HOSTNAME
+ if (netif->hostname != NULL) {
+ const char *p = (const char*)netif->hostname;
+ u8_t namelen = (u8_t)strlen(p);
+ if (namelen > 0) {
+ LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
+ dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
+ while (*p) {
+ dhcp_option_byte(dhcp, *p++);
+ }
+ }
+ }
+#endif /* LWIP_NETIF_HOSTNAME */
+
+#if 0
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
+
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
+#endif
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* broadcast to server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Enter REBOOTING state to verify an existing lease
+ *
+ * @param netif network interface which must reboot
+ */
+static err_t
+dhcp_reboot(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n"));
+ dhcp_set_state(dhcp, DHCP_REBOOTING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, 576);
+
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* broadcast to server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * Release a DHCP lease.
+ *
+ * @param netif network interface which must release its lease
+ */
+err_t
+dhcp_release(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n"));
+
+ /* idle DHCP client */
+ dhcp_set_state(dhcp, DHCP_OFF);
+ /* clean old DHCP offer */
+ ip_addr_set_zero(&dhcp->server_ip_addr);
+ ip_addr_set_zero(&dhcp->offered_ip_addr);
+ ip_addr_set_zero(&dhcp->offered_sn_mask);
+ ip_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE);
+ if (result == ERR_OK) {
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs));
+ /* bring the interface down */
+ netif_set_down(netif);
+ /* remove IP address from interface */
+ netif_set_ipaddr(netif, IP_ADDR_ANY);
+ netif_set_gw(netif, IP_ADDR_ANY);
+ netif_set_netmask(netif, IP_ADDR_ANY);
+
+ return result;
+}
+
+/**
+ * Remove the DHCP client from the interface.
+ *
+ * @param netif The network interface to stop DHCP on
+ */
+void
+dhcp_stop(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;);
+ dhcp = netif->dhcp;
+ /* Remove the flag that says this netif is handled by DHCP. */
+ netif->flags &= ~NETIF_FLAG_DHCP;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n"));
+ /* netif is DHCP configured? */
+ if (dhcp != NULL) {
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ if (dhcp->pcb != NULL) {
+ udp_remove(dhcp->pcb);
+ dhcp->pcb = NULL;
+ }
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+ dhcp_set_state(dhcp, DHCP_OFF);
+ }
+}
+
+/*
+ * Set the DHCP state of a DHCP client.
+ *
+ * If the state changed, reset the number of tries.
+ */
+static void
+dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
+{
+ if (new_state != dhcp->state) {
+ dhcp->state = new_state;
+ dhcp->tries = 0;
+ dhcp->request_timeout = 0;
+ }
+}
+
+/*
+ * Concatenate an option type and length field to the outgoing
+ * DHCP message.
+ *
+ */
+static void
+dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len)
+{
+ LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = option_type;
+ dhcp->msg_out->options[dhcp->options_out_len++] = option_len;
+}
+/*
+ * Concatenate a single byte to the outgoing DHCP message.
+ *
+ */
+static void
+dhcp_option_byte(struct dhcp *dhcp, u8_t value)
+{
+ LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = value;
+}
+
+static void
+dhcp_option_short(struct dhcp *dhcp, u16_t value)
+{
+ LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU);
+}
+
+static void
+dhcp_option_long(struct dhcp *dhcp, u32_t value)
+{
+ LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL));
+}
+
+/**
+ * Extract the DHCP message and the DHCP options.
+ *
+ * Extract the DHCP message and the DHCP options, each into a contiguous
+ * piece of memory. As a DHCP message is variable sized by its options,
+ * and also allows overriding some fields for options, the easy approach
+ * is to first unfold the options into a conitguous piece of memory, and
+ * use that further on.
+ *
+ */
+static err_t
+dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p)
+{
+ u8_t *options;
+ u16_t offset;
+ u16_t offset_max;
+ u16_t options_idx;
+ u16_t options_idx_max;
+ struct pbuf *q;
+ int parse_file_as_options = 0;
+ int parse_sname_as_options = 0;
+
+ /* clear received options */
+ dhcp_clear_all_options(dhcp);
+ /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */
+ if (p->len < DHCP_SNAME_OFS) {
+ return ERR_BUF;
+ }
+ dhcp->msg_in = (struct dhcp_msg *)p->payload;
+#if LWIP_DHCP_BOOTP_FILE
+ /* clear boot file name */
+ dhcp->boot_file_name[0] = 0;
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* parse options */
+
+ /* start with options field */
+ options_idx = DHCP_OPTIONS_OFS;
+ /* parse options to the end of the received packet */
+ options_idx_max = p->tot_len;
+again:
+ q = p;
+ while((q != NULL) && (options_idx >= q->len)) {
+ options_idx -= q->len;
+ options_idx_max -= q->len;
+ q = q->next;
+ }
+ if (q == NULL) {
+ return ERR_BUF;
+ }
+ offset = options_idx;
+ offset_max = options_idx_max;
+ options = (u8_t*)q->payload;
+ /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
+ while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) {
+ u8_t op = options[offset];
+ u8_t len;
+ u8_t decode_len = 0;
+ int decode_idx = -1;
+ u16_t val_offset = offset + 2;
+ /* len byte might be in the next pbuf */
+ if (offset + 1 < q->len) {
+ len = options[offset + 1];
+ } else {
+ len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0);
+ }
+ /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */
+ decode_len = len;
+ switch(op) {
+ /* case(DHCP_OPTION_END): handled above */
+ case(DHCP_OPTION_PAD):
+ /* special option: no len encoded */
+ decode_len = len = 0;
+ /* will be increased below */
+ offset--;
+ break;
+ case(DHCP_OPTION_SUBNET_MASK):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
+ break;
+ case(DHCP_OPTION_ROUTER):
+ decode_len = 4; /* only copy the first given router */
+ LWIP_ASSERT("len >= decode_len", len >= decode_len);
+ decode_idx = DHCP_OPTION_IDX_ROUTER;
+ break;
+ case(DHCP_OPTION_DNS_SERVER):
+ /* special case: there might be more than one server */
+ LWIP_ASSERT("len % 4 == 0", len % 4 == 0);
+ /* limit number of DNS servers */
+ decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
+ LWIP_ASSERT("len >= decode_len", len >= decode_len);
+ decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
+ break;
+ case(DHCP_OPTION_LEASE_TIME):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
+ break;
+ case(DHCP_OPTION_OVERLOAD):
+ LWIP_ASSERT("len == 1", len == 1);
+ decode_idx = DHCP_OPTION_IDX_OVERLOAD;
+ break;
+ case(DHCP_OPTION_MESSAGE_TYPE):
+ LWIP_ASSERT("len == 1", len == 1);
+ decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
+ break;
+ case(DHCP_OPTION_SERVER_ID):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_SERVER_ID;
+ break;
+ case(DHCP_OPTION_T1):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_T1;
+ break;
+ case(DHCP_OPTION_T2):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_T2;
+ break;
+ default:
+ decode_len = 0;
+ LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op));
+ break;
+ }
+ offset += len + 2;
+ if (decode_len > 0) {
+ u32_t value = 0;
+ u16_t copy_len;
+decode_next:
+ LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
+ LWIP_ASSERT("option already decoded", !dhcp_option_given(dhcp, decode_idx));
+ copy_len = LWIP_MIN(decode_len, 4);
+ pbuf_copy_partial(q, &value, copy_len, val_offset);
+ if (decode_len > 4) {
+ /* decode more than one u32_t */
+ LWIP_ASSERT("decode_len % 4 == 0", decode_len % 4 == 0);
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, htonl(value));
+ decode_len -= 4;
+ val_offset += 4;
+ decode_idx++;
+ goto decode_next;
+ } else if (decode_len == 4) {
+ value = ntohl(value);
+ } else {
+ LWIP_ASSERT("invalid decode_len", decode_len == 1);
+ value = ((u8_t*)&value)[0];
+ }
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, value);
+ }
+ if (offset >= q->len) {
+ offset -= q->len;
+ offset_max -= q->len;
+ q = q->next;
+ options = (u8_t*)q->payload;
+ }
+ }
+ /* is this an overloaded message? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) {
+ u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ if (overload == DHCP_OVERLOAD_FILE) {
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME) {
+ parse_sname_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME_FILE) {
+ parse_sname_as_options = 1;
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload));
+ }
+#if LWIP_DHCP_BOOTP_FILE
+ if (!parse_file_as_options) {
+ /* only do this for ACK messages */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) &&
+ (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK))
+ /* copy bootp file name, don't care for sname (server hostname) */
+ pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS);
+ /* make sure the string is really NULL-terminated */
+ dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
+ }
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ }
+ if (parse_file_as_options) {
+ /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */
+ parse_file_as_options = 0;
+ options_idx = DHCP_FILE_OFS;
+ options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN;
+ goto again;
+ } else if (parse_sname_as_options) {
+ parse_sname_as_options = 0;
+ options_idx = DHCP_SNAME_OFS;
+ options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN;
+ goto again;
+ }
+ return ERR_OK;
+}
+
+/**
+ * If an incoming DHCP message is in response to us, then trigger the state machine
+ */
+static void
+dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+{
+ struct netif *netif = (struct netif *)arg;
+ struct dhcp *dhcp = netif->dhcp;
+ struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload;
+ u8_t msg_type;
+ u8_t i;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p,
+ ip4_addr1_16(addr), ip4_addr2_16(addr), ip4_addr3_16(addr), ip4_addr4_16(addr), port));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+ /* prevent warnings about unused arguments */
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+
+ if (p->len < DHCP_MIN_REPLY_LEN) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n"));
+ goto free_pbuf_and_return;
+ }
+
+ if (reply_msg->op != DHCP_BOOTREPLY) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
+ goto free_pbuf_and_return;
+ }
+ /* iterate through hardware address and match against DHCP message */
+ for (i = 0; i < netif->hwaddr_len; i++) {
+ if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n",
+ (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i]));
+ goto free_pbuf_and_return;
+ }
+ }
+ /* match transaction ID against what we expected */
+ if (ntohl(reply_msg->xid) != dhcp->xid) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid));
+ goto free_pbuf_and_return;
+ }
+ /* option fields could be unfold? */
+ if (dhcp_parse_reply(dhcp, p) != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("problem unfolding DHCP message - too short on memory?\n"));
+ goto free_pbuf_and_return;
+ }
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
+ /* obtain pointer to DHCP message type */
+ if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
+ goto free_pbuf_and_return;
+ }
+
+ /* read DHCP message type */
+ msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
+ /* message type is DHCP ACK? */
+ if (msg_type == DHCP_ACK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
+ /* in requesting state? */
+ if (dhcp->state == DHCP_REQUESTING) {
+ dhcp_handle_ack(netif);
+#if DHCP_DOES_ARP_CHECK
+ /* check if the acknowledged lease address is already in use */
+ dhcp_check(netif);
+#else
+ /* bind interface to the acknowledged lease address */
+ dhcp_bind(netif);
+#endif
+ }
+ /* already bound to the given lease address? */
+ else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) {
+ dhcp_bind(netif);
+ }
+ }
+ /* received a DHCP_NAK in appropriate state? */
+ else if ((msg_type == DHCP_NAK) &&
+ ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) ||
+ (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
+ dhcp_handle_nak(netif);
+ }
+ /* received a DHCP_OFFER in DHCP_SELECTING state? */
+ else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n"));
+ dhcp->request_timeout = 0;
+ /* remember offered lease */
+ dhcp_handle_offer(netif);
+ }
+free_pbuf_and_return:
+ dhcp->msg_in = NULL;
+ pbuf_free(p);
+}
+
+/**
+ * Create a DHCP request, fill in common headers
+ *
+ * @param netif the netif under DHCP control
+ * @param dhcp dhcp control struct
+ * @param message_type message type of the request
+ */
+static err_t
+dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
+{
+ u16_t i;
+#ifndef DHCP_GLOBAL_XID
+ /** default global transaction identifier starting value (easy to match
+ * with a packet analyser). We simply increment for each new request.
+ * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one
+ * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */
+ static u32_t xid = 0xABCD0000;
+#else
+ static u32_t xid;
+ static u8_t xid_initialised = 0;
+ if (!xid_initialised) {
+ xid = DHCP_GLOBAL_XID;
+ xid_initialised = !xid_initialised;
+ }
+#endif
+ LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;);
+ LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
+ LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL);
+ LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL);
+ dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
+ if (dhcp->p_out == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_create_msg(): could not allocate pbuf\n"));
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
+ (dhcp->p_out->len >= sizeof(struct dhcp_msg)));
+
+ /* reuse transaction identifier in retransmissions */
+ if (dhcp->tries == 0) {
+ xid++;
+ }
+ dhcp->xid = xid;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
+ ("transaction id xid(%"X32_F")\n", xid));
+
+ dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload;
+
+ dhcp->msg_out->op = DHCP_BOOTREQUEST;
+ /* TODO: make link layer independent */
+ dhcp->msg_out->htype = DHCP_HTYPE_ETH;
+ dhcp->msg_out->hlen = netif->hwaddr_len;
+ dhcp->msg_out->hops = 0;
+ dhcp->msg_out->xid = htonl(dhcp->xid);
+ dhcp->msg_out->secs = 0;
+ /* we don't need the broadcast flag since we can receive unicast traffic
+ before being fully configured! */
+ dhcp->msg_out->flags = 0;
+ ip_addr_set_zero(&dhcp->msg_out->ciaddr);
+ /* set ciaddr to netif->ip_addr based on message_type and state */
+ if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) ||
+ ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */
+ ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) {
+ ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr);
+ }
+ ip_addr_set_zero(&dhcp->msg_out->yiaddr);
+ ip_addr_set_zero(&dhcp->msg_out->siaddr);
+ ip_addr_set_zero(&dhcp->msg_out->giaddr);
+ for (i = 0; i < DHCP_CHADDR_LEN; i++) {
+ /* copy netif hardware address, pad with zeroes */
+ dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len) ? netif->hwaddr[i] : 0/* pad byte*/;
+ }
+ for (i = 0; i < DHCP_SNAME_LEN; i++) {
+ dhcp->msg_out->sname[i] = 0;
+ }
+ for (i = 0; i < DHCP_FILE_LEN; i++) {
+ dhcp->msg_out->file[i] = 0;
+ }
+ dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
+ dhcp->options_out_len = 0;
+ /* fill options field with an incrementing array (for debugging purposes) */
+ for (i = 0; i < DHCP_OPTIONS_LEN; i++) {
+ dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */
+ }
+ /* Add option MESSAGE_TYPE */
+ dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+ dhcp_option_byte(dhcp, message_type);
+ return ERR_OK;
+}
+
+/**
+ * Free previously allocated memory used to send a DHCP request.
+ *
+ * @param dhcp the dhcp struct to free the request from
+ */
+static void
+dhcp_delete_msg(struct dhcp *dhcp)
+{
+ LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL);
+ LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL);
+ if (dhcp->p_out != NULL) {
+ pbuf_free(dhcp->p_out);
+ }
+ dhcp->p_out = NULL;
+ dhcp->msg_out = NULL;
+}
+
+/**
+ * Add a DHCP message trailer
+ *
+ * Adds the END option to the DHCP message, and if
+ * necessary, up to three padding bytes.
+ *
+ * @param dhcp DHCP state structure
+ */
+static void
+dhcp_option_trailer(struct dhcp *dhcp)
+{
+ LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL);
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END;
+ /* packet is too small, or not 4 byte aligned? */
+ while ((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) {
+ /* LWIP_DEBUGF(DHCP_DEBUG,("dhcp_option_trailer:dhcp->options_out_len=%"U16_F", DHCP_OPTIONS_LEN=%"U16_F, dhcp->options_out_len, DHCP_OPTIONS_LEN)); */
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ /* add a fill/padding byte */
+ dhcp->msg_out->options[dhcp->options_out_len++] = 0;
+ }
+}
+
+#endif /* LWIP_DHCP */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/dns.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,969 @@
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ *
+ */
+
+/**
+
+ * This file implements a DNS host name to IP address resolver.
+
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+
+ * uIP version Copyright (c) 2002-2003, Adam Dunkels.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * DNS.C
+ *
+ * The lwIP DNS resolver functions are used to lookup a host name and
+ * map it to a numerical IP address. It maintains a list of resolved
+ * hostnames that can be queried with the dns_lookup() function.
+ * New hostnames can be resolved using the dns_query() function.
+ *
+ * The lwIP version of the resolver also adds a non-blocking version of
+ * gethostbyname() that will work with a raw API application. This function
+ * checks for an IP address string first and converts it if it is valid.
+ * gethostbyname() then does a dns_lookup() to see if the name is
+ * already in the table. If so, the IP is returned. If not, a query is
+ * issued and the function returns with a ERR_INPROGRESS status. The app
+ * using the dns client must then go into a waiting state.
+ *
+ * Once a hostname has been resolved (or found to be non-existent),
+ * the resolver code calls a specified callback function (which
+ * must be implemented by the module that uses the resolver).
+ */
+
+/*-----------------------------------------------------------------------------
+ * RFC 1035 - Domain names - implementation and specification
+ * RFC 2181 - Clarifications to the DNS Specification
+ *----------------------------------------------------------------------------*/
+
+/** @todo: define good default values (rfc compliance) */
+/** @todo: improve answer parsing, more checkings... */
+/** @todo: check RFC1035 - 7.3. Processing responses */
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+
+/** DNS server IP address */
+#ifndef DNS_SERVER_ADDRESS
+#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
+#endif
+
+/** DNS server port address */
+#ifndef DNS_SERVER_PORT
+#define DNS_SERVER_PORT 53
+#endif
+
+/** DNS maximum number of retries when asking for a name, before "timeout". */
+#ifndef DNS_MAX_RETRIES
+#define DNS_MAX_RETRIES 4
+#endif
+
+/** DNS resource record max. TTL (one week as default) */
+#ifndef DNS_MAX_TTL
+#define DNS_MAX_TTL 604800
+#endif
+
+/* DNS protocol flags */
+#define DNS_FLAG1_RESPONSE 0x80
+#define DNS_FLAG1_OPCODE_STATUS 0x10
+#define DNS_FLAG1_OPCODE_INVERSE 0x08
+#define DNS_FLAG1_OPCODE_STANDARD 0x00
+#define DNS_FLAG1_AUTHORATIVE 0x04
+#define DNS_FLAG1_TRUNC 0x02
+#define DNS_FLAG1_RD 0x01
+#define DNS_FLAG2_RA 0x80
+#define DNS_FLAG2_ERR_MASK 0x0f
+#define DNS_FLAG2_ERR_NONE 0x00
+#define DNS_FLAG2_ERR_NAME 0x03
+
+/* DNS protocol states */
+#define DNS_STATE_UNUSED 0
+#define DNS_STATE_NEW 1
+#define DNS_STATE_ASKING 2
+#define DNS_STATE_DONE 3
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** DNS message header */
+struct dns_hdr {
+ PACK_STRUCT_FIELD(u16_t id);
+ PACK_STRUCT_FIELD(u8_t flags1);
+ PACK_STRUCT_FIELD(u8_t flags2);
+ PACK_STRUCT_FIELD(u16_t numquestions);
+ PACK_STRUCT_FIELD(u16_t numanswers);
+ PACK_STRUCT_FIELD(u16_t numauthrr);
+ PACK_STRUCT_FIELD(u16_t numextrarr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#define SIZEOF_DNS_HDR 12
+
+/** DNS query message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_query {
+ /* DNS query record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+};
+#define SIZEOF_DNS_QUERY 4
+
+/** DNS answer message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_answer {
+ /* DNS answer record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+ u32_t ttl;
+ u16_t len;
+};
+#define SIZEOF_DNS_ANSWER 10
+
+/** DNS table entry */
+struct dns_table_entry {
+ u8_t state;
+ u8_t numdns;
+ u8_t tmr;
+ u8_t retries;
+ u8_t seqno;
+ u8_t err;
+ u32_t ttl;
+ char name[DNS_MAX_NAME_LENGTH];
+ ip_addr_t ipaddr;
+ /* pointer to callback on DNS query done */
+ dns_found_callback found;
+ void *arg;
+};
+
+#if DNS_LOCAL_HOSTLIST
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Local host-list. For hostnames in this list, no
+ * external name resolution is performed */
+static struct local_hostlist_entry *local_hostlist_dynamic;
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
+#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
+#define DNS_LOCAL_HOSTLIST_STORAGE_POST
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
+DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
+ DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
+
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+static void dns_init_local();
+#endif /* DNS_LOCAL_HOSTLIST */
+
+
+/* forward declarations */
+static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+static void dns_check_entries(void);
+
+/*-----------------------------------------------------------------------------
+ * Globales
+ *----------------------------------------------------------------------------*/
+
+/* DNS variables */
+static struct udp_pcb *dns_pcb;
+static u8_t dns_seqno;
+static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
+static ip_addr_t dns_servers[DNS_MAX_SERVERS];
+/** Contiguous buffer for processing responses */
+static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
+static u8_t* dns_payload;
+
+/**
+ * Initialize the resolver: set up the UDP pcb and configure the default server
+ * (DNS_SERVER_ADDRESS).
+ */
+void
+dns_init()
+{
+ ip_addr_t dnsserver;
+
+ dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer);
+
+ /* initialize default DNS server address */
+ DNS_SERVER_ADDRESS(&dnsserver);
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
+
+ /* if dns client not yet initialized... */
+ if (dns_pcb == NULL) {
+ dns_pcb = udp_new();
+
+ if (dns_pcb != NULL) {
+ /* initialize DNS table not needed (initialized to zero since it is a
+ * global variable) */
+ LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
+ DNS_STATE_UNUSED == 0);
+
+ /* initialize DNS client */
+ udp_bind(dns_pcb, IP_ADDR_ANY, 0);
+ udp_recv(dns_pcb, dns_recv, NULL);
+
+ /* initialize default DNS primary server */
+ dns_setserver(0, &dnsserver);
+ }
+ }
+#if DNS_LOCAL_HOSTLIST
+ dns_init_local();
+#endif
+}
+
+/**
+ * Initialize one of the DNS servers.
+ *
+ * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
+ * @param dnsserver IP address of the DNS server to set
+ */
+void
+dns_setserver(u8_t numdns, ip_addr_t *dnsserver)
+{
+ if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) &&
+ (dnsserver != NULL) && !ip_addr_isany(dnsserver)) {
+ dns_servers[numdns] = (*dnsserver);
+ }
+}
+
+/**
+ * Obtain one of the currently configured DNS server.
+ *
+ * @param numdns the index of the DNS server
+ * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
+ * server has not been configured.
+ */
+ip_addr_t
+dns_getserver(u8_t numdns)
+{
+ if (numdns < DNS_MAX_SERVERS) {
+ return dns_servers[numdns];
+ } else {
+ return *IP_ADDR_ANY;
+ }
+}
+
+/**
+ * The DNS resolver client timer - handle retries and timeouts and should
+ * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
+ */
+void
+dns_tmr(void)
+{
+ if (dns_pcb != NULL) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
+ dns_check_entries();
+ }
+}
+
+#if DNS_LOCAL_HOSTLIST
+static void
+dns_init_local()
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
+ int i;
+ struct local_hostlist_entry *entry;
+ /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
+ struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
+ size_t namelen;
+ for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) {
+ struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
+ LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
+ namelen = strlen(init_entry->name);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
+ if (entry != NULL) {
+ entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY((char*)entry->name, init_entry->name, namelen);
+ ((char*)entry->name)[namelen] = 0;
+ entry->addr = init_entry->addr;
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
+}
+
+/**
+ * Scans the local host-list for a hostname.
+ *
+ * @param hostname Hostname to look for in the local host-list
+ * @return The first IP address for the hostname in the local host-list or
+ * IPADDR_NONE if not found.
+ */
+static u32_t
+dns_lookup_local(const char *hostname)
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ while(entry != NULL) {
+ if(strcmp(entry->name, hostname) == 0) {
+ return ip4_addr_get_u32(&entry->addr);
+ }
+ entry = entry->next;
+ }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ int i;
+ for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) {
+ if(strcmp(local_hostlist_static[i].name, hostname) == 0) {
+ return ip4_addr_get_u32(&local_hostlist_static[i].addr);
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ return IPADDR_NONE;
+}
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Remove all entries from the local host-list for a specific hostname
+ * and/or IP addess
+ *
+ * @param hostname hostname for which entries shall be removed from the local
+ * host-list
+ * @param addr address for which entries shall be removed from the local host-list
+ * @return the number of removed entries
+ */
+int
+dns_local_removehost(const char *hostname, const ip_addr_t *addr)
+{
+ int removed = 0;
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ struct local_hostlist_entry *last_entry = NULL;
+ while (entry != NULL) {
+ if (((hostname == NULL) || !strcmp(entry->name, hostname)) &&
+ ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
+ struct local_hostlist_entry *free_entry;
+ if (last_entry != NULL) {
+ last_entry->next = entry->next;
+ } else {
+ local_hostlist_dynamic = entry->next;
+ }
+ free_entry = entry;
+ entry = entry->next;
+ memp_free(MEMP_LOCALHOSTLIST, free_entry);
+ removed++;
+ } else {
+ last_entry = entry;
+ entry = entry->next;
+ }
+ }
+ return removed;
+}
+
+/**
+ * Add a hostname/IP address pair to the local host-list.
+ * Duplicates are not checked.
+ *
+ * @param hostname hostname of the new entry
+ * @param addr IP address of the new entry
+ * @return ERR_OK if succeeded or ERR_MEM on memory error
+ */
+err_t
+dns_local_addhost(const char *hostname, const ip_addr_t *addr)
+{
+ struct local_hostlist_entry *entry;
+ size_t namelen;
+ LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
+ namelen = strlen(hostname);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ if (entry == NULL) {
+ return ERR_MEM;
+ }
+ entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY((char*)entry->name, hostname, namelen);
+ ((char*)entry->name)[namelen] = 0;
+ ip_addr_copy(entry->addr, *addr);
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ return ERR_OK;
+}
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/**
+ * Look up a hostname in the array of known hostnames.
+ *
+ * @note This function only looks in the internal array of known
+ * hostnames, it does not send out a query for the hostname if none
+ * was found. The function dns_enqueue() can be used to send a query
+ * for a hostname.
+ *
+ * @param name the hostname to look up
+ * @return the hostname's IP address, as u32_t (instead of ip_addr_t to
+ * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
+ * was not found in the cached dns_table.
+ */
+static u32_t
+dns_lookup(const char *name)
+{
+ u8_t i;
+#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
+ u32_t addr;
+#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
+#if DNS_LOCAL_HOSTLIST
+ if ((addr = dns_lookup_local(name)) != IPADDR_NONE) {
+ return addr;
+ }
+#endif /* DNS_LOCAL_HOSTLIST */
+#ifdef DNS_LOOKUP_LOCAL_EXTERN
+ if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) {
+ return addr;
+ }
+#endif /* DNS_LOOKUP_LOCAL_EXTERN */
+
+ /* Walk through name list, return entry if found. If not, return NULL. */
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ if ((dns_table[i].state == DNS_STATE_DONE) &&
+ (strcmp(name, dns_table[i].name) == 0)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
+ ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+ return ip4_addr_get_u32(&dns_table[i].ipaddr);
+ }
+ }
+
+ return IPADDR_NONE;
+}
+
+#if DNS_DOES_NAME_CHECK
+/**
+ * Compare the "dotted" name "query" with the encoded name "response"
+ * to make sure an answer from the DNS server matches the current dns_table
+ * entry (otherwise, answers might arrive late for hostname not on the list
+ * any more).
+ *
+ * @param query hostname (not encoded) from the dns_table
+ * @param response encoded hostname in the DNS response
+ * @return 0: names equal; 1: names differ
+ */
+static u8_t
+dns_compare_name(unsigned char *query, unsigned char *response)
+{
+ unsigned char n;
+
+ do {
+ n = *response++;
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name */
+ break;
+ } else {
+ /* Not compressed name */
+ while (n > 0) {
+ if ((*query) != (*response)) {
+ return 1;
+ }
+ ++response;
+ ++query;
+ --n;
+ };
+ ++query;
+ }
+ } while (*response != 0);
+
+ return 0;
+}
+#endif /* DNS_DOES_NAME_CHECK */
+
+/**
+ * Walk through a compact encoded DNS name and return the end of the name.
+ *
+ * @param query encoded DNS name in the DNS server response
+ * @return end of the name
+ */
+static unsigned char *
+dns_parse_name(unsigned char *query)
+{
+ unsigned char n;
+
+ do {
+ n = *query++;
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name */
+ break;
+ } else {
+ /* Not compressed name */
+ while (n > 0) {
+ ++query;
+ --n;
+ };
+ }
+ } while (*query != 0);
+
+ return query + 1;
+}
+
+/**
+ * Send a DNS query packet.
+ *
+ * @param numdns index of the DNS server in the dns_servers table
+ * @param name hostname to query
+ * @param id index of the hostname in dns_table, used as transaction ID in the
+ * DNS query packet
+ * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
+ */
+static err_t
+dns_send(u8_t numdns, const char* name, u8_t id)
+{
+ err_t err;
+ struct dns_hdr *hdr;
+ struct dns_query qry;
+ struct pbuf *p;
+ char *query, *nptr;
+ const char *pHostname;
+ u8_t n;
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
+ (u16_t)(numdns), name));
+ LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);
+ LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns]));
+
+ /* if here, we have either a new query or a retry on a previous query to process */
+ p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH +
+ SIZEOF_DNS_QUERY, PBUF_RAM);
+ if (p != NULL) {
+ LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
+ /* fill dns header */
+ hdr = (struct dns_hdr*)p->payload;
+ memset(hdr, 0, SIZEOF_DNS_HDR);
+ hdr->id = htons(id);
+ hdr->flags1 = DNS_FLAG1_RD;
+ hdr->numquestions = PP_HTONS(1);
+ query = (char*)hdr + SIZEOF_DNS_HDR;
+ pHostname = name;
+ --pHostname;
+
+ /* convert hostname into suitable query format. */
+ do {
+ ++pHostname;
+ nptr = query;
+ ++query;
+ for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
+ *query = *pHostname;
+ ++query;
+ ++n;
+ }
+ *nptr = n;
+ } while(*pHostname != 0);
+ *query++='\0';
+
+ /* fill dns query */
+ qry.type = PP_HTONS(DNS_RRTYPE_A);
+ qry.cls = PP_HTONS(DNS_RRCLASS_IN);
+ SMEMCPY(query, &qry, SIZEOF_DNS_QUERY);
+
+ /* resize pbuf to the exact dns query */
+ pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))));
+
+ /* connect to the server for faster receiving */
+ udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);
+ /* send dns packet */
+ err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);
+
+ /* free pbuf */
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+
+ return err;
+}
+
+/**
+ * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.
+ * Check an entry in the dns_table:
+ * - send out query for new entries
+ * - retry old pending entries on timeout (also with different servers)
+ * - remove completed entries from the table if their TTL has expired
+ *
+ * @param i index of the dns_table entry to check
+ */
+static void
+dns_check_entry(u8_t i)
+{
+ err_t err;
+ struct dns_table_entry *pEntry = &dns_table[i];
+
+ LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
+
+ switch(pEntry->state) {
+
+ case DNS_STATE_NEW: {
+ /* initialize new entry */
+ pEntry->state = DNS_STATE_ASKING;
+ pEntry->numdns = 0;
+ pEntry->tmr = 1;
+ pEntry->retries = 0;
+
+ /* send DNS packet for this entry */
+ err = dns_send(pEntry->numdns, pEntry->name, i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %d\n", err)); //FIX DG, lwip_strerr is part of lwip/api
+ }
+ break;
+ }
+
+ case DNS_STATE_ASKING: {
+ if (--pEntry->tmr == 0) {
+ if (++pEntry->retries == DNS_MAX_RETRIES) {
+ if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) {
+ /* change of server */
+ pEntry->numdns++;
+ pEntry->tmr = 1;
+ pEntry->retries = 0;
+ break;
+ } else {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
+ /* call specified callback function if provided */
+ if (pEntry->found)
+ (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+ break;
+ }
+ }
+
+ /* wait longer for the next retry */
+ pEntry->tmr = pEntry->retries;
+
+ /* send DNS packet for this entry */
+ err = dns_send(pEntry->numdns, pEntry->name, i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %d\n", err)); //FIX DG, lwip_strerr is part of lwip/api
+ }
+ }
+ break;
+ }
+
+ case DNS_STATE_DONE: {
+ /* if the time to live is nul */
+ if (--pEntry->ttl == 0) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+ }
+ break;
+ }
+ case DNS_STATE_UNUSED:
+ /* nothing to do */
+ break;
+ default:
+ LWIP_ASSERT("unknown dns_table entry state:", 0);
+ break;
+ }
+}
+
+/**
+ * Call dns_check_entry for each entry in dns_table - check all entries.
+ */
+static void
+dns_check_entries(void)
+{
+ u8_t i;
+
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ dns_check_entry(i);
+ }
+}
+
+/**
+ * Receive input function for DNS response packets arriving for the dns UDP pcb.
+ *
+ * @params see udp.h
+ */
+static void
+dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+{
+ u16_t i;
+ char *pHostname;
+ struct dns_hdr *hdr;
+ struct dns_answer ans;
+ struct dns_table_entry *pEntry;
+ u16_t nquestions, nanswers;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ /* is the dns message too big ? */
+ if (p->tot_len > DNS_MSG_SIZE) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
+ /* free pbuf and return */
+ goto memerr;
+ }
+
+ /* is the dns message big enough ? */
+ if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
+ /* free pbuf and return */
+ goto memerr;
+ }
+
+ /* copy dns payload inside static buffer for processing */
+ if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) {
+ /* The ID in the DNS header should be our entry into the name table. */
+ hdr = (struct dns_hdr*)dns_payload;
+ i = htons(hdr->id);
+ if (i < DNS_TABLE_SIZE) {
+ pEntry = &dns_table[i];
+ if(pEntry->state == DNS_STATE_ASKING) {
+ /* This entry is now completed. */
+ pEntry->state = DNS_STATE_DONE;
+ pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
+
+ /* We only care about the question(s) and the answers. The authrr
+ and the extrarr are simply discarded. */
+ nquestions = htons(hdr->numquestions);
+ nanswers = htons(hdr->numanswers);
+
+ /* Check for error. If so, call callback to inform. */
+ if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+
+#if DNS_DOES_NAME_CHECK
+ /* Check if the name in the "question" part match with the name in the entry. */
+ if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+#endif /* DNS_DOES_NAME_CHECK */
+
+ /* Skip the name in the "question" part */
+ pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY;
+
+ while (nanswers > 0) {
+ /* skip answer resource record's host name */
+ pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
+
+ /* Check for IP address type and Internet class. Others are discarded. */
+ SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER);
+ if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) &&
+ (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) {
+ /* read the answer resource record's TTL, and maximize it if needed */
+ pEntry->ttl = ntohl(ans.ttl);
+ if (pEntry->ttl > DNS_MAX_TTL) {
+ pEntry->ttl = DNS_MAX_TTL;
+ }
+ /* read the IP address after answer resource record's header */
+ SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t));
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
+ ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+ /* call specified callback function if provided */
+ if (pEntry->found) {
+ (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
+ }
+ /* deallocate memory and return */
+ goto memerr;
+ } else {
+ pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len);
+ }
+ --nanswers;
+ }
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+ }
+ }
+
+ /* deallocate memory and return */
+ goto memerr;
+
+responseerr:
+ /* ERROR: call specified callback function with NULL as name to indicate an error */
+ if (pEntry->found) {
+ (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+ }
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+
+memerr:
+ /* free pbuf */
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * Queues a new hostname to resolve and sends out a DNS query for that hostname
+ *
+ * @param name the hostname that is to be queried
+ * @param found a callback founction to be called on success, failure or timeout
+ * @param callback_arg argument to pass to the callback function
+ * @return @return a err_t return code.
+ */
+static err_t
+dns_enqueue(const char *name, dns_found_callback found, void *callback_arg)
+{
+ u8_t i;
+ u8_t lseq, lseqi;
+ struct dns_table_entry *pEntry = NULL;
+ size_t namelen;
+
+ /* search an unused entry, or the oldest one */
+ lseq = lseqi = 0;
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ pEntry = &dns_table[i];
+ /* is it an unused entry ? */
+ if (pEntry->state == DNS_STATE_UNUSED)
+ break;
+
+ /* check if this is the oldest completed entry */
+ if (pEntry->state == DNS_STATE_DONE) {
+ if ((dns_seqno - pEntry->seqno) > lseq) {
+ lseq = dns_seqno - pEntry->seqno;
+ lseqi = i;
+ }
+ }
+ }
+
+ /* if we don't have found an unused entry, use the oldest completed one */
+ if (i == DNS_TABLE_SIZE) {
+ if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
+ /* no entry can't be used now, table is full */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
+ return ERR_MEM;
+ } else {
+ /* use the oldest completed one */
+ i = lseqi;
+ pEntry = &dns_table[i];
+ }
+ }
+
+ /* use this entry */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
+
+ /* fill the entry */
+ pEntry->state = DNS_STATE_NEW;
+ pEntry->seqno = dns_seqno++;
+ pEntry->found = found;
+ pEntry->arg = callback_arg;
+ namelen = LWIP_MIN(strlen(name), DNS_MAX_NAME_LENGTH-1);
+ MEMCPY(pEntry->name, name, namelen);
+ pEntry->name[namelen] = 0;
+
+ /* force to send query without waiting timer */
+ dns_check_entry(i);
+
+ /* dns query is enqueued */
+ return ERR_INPROGRESS;
+}
+
+/**
+ * Resolve a hostname (string) into an IP address.
+ * NON-BLOCKING callback version for use with raw API!!!
+ *
+ * Returns immediately with one of err_t return codes:
+ * - ERR_OK if hostname is a valid IP address string or the host
+ * name is already in the local names table.
+ * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
+ * for resolution if no errors are present.
+ *
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ * cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ * ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @return a err_t return code.
+ */
+err_t
+dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+ void *callback_arg)
+{
+ u32_t ipaddr;
+ /* not initialized or no valid server yet, or invalid addr pointer
+ * or invalid hostname or invalid hostname length */
+ if ((dns_pcb == NULL) || (addr == NULL) ||
+ (!hostname) || (!hostname[0]) ||
+ (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) {
+ return ERR_VAL;
+ }
+
+#if LWIP_HAVE_LOOPIF
+ if (strcmp(hostname, "localhost")==0) {
+ ip_addr_set_loopback(addr);
+ return ERR_OK;
+ }
+#endif /* LWIP_HAVE_LOOPIF */
+
+ /* host name already in octet notation? set ip addr and return ERR_OK */
+ ipaddr = ipaddr_addr(hostname);
+ if (ipaddr == IPADDR_NONE) {
+ /* already have this address cached? */
+ ipaddr = dns_lookup(hostname);
+ }
+ if (ipaddr != IPADDR_NONE) {
+ ip4_addr_set_u32(addr, ipaddr);
+ return ERR_OK;
+ }
+
+ /* queue query with specified callback */
+ return dns_enqueue(hostname, found, callback_arg);
+}
+
+#endif /* LWIP_DNS */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/init.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,303 @@
+/**
+ * @file
+ * Modules initialization
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/init.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/sockets.h"
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/autoip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/timers.h"
+#include "netif/etharp.h"
+
+/* Compile-time sanity checks for configuration errors.
+ * These can be done independently of LWIP_DEBUG, without penalty.
+ */
+#ifndef BYTE_ORDER
+ #error "BYTE_ORDER is not defined, you have to define it in your cc.h"
+#endif
+#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV)
+ #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && ARP_QUEUEING)
+ #error "If you want to use ARP Queueing, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_UDPLITE)
+ #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+ #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DHCP)
+ #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_IGMP)
+ #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+ #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DNS)
+ #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0))
+ #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h"
+#endif
+#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0))
+ #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0))
+ #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0))
+ #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_WND > 0xffff))
+ #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
+ #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
+ #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
+#endif
+#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff))
+ #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
+#endif
+#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
+ #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
+#endif
+#if (LWIP_NETIF_API && (NO_SYS==1))
+ #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
+ #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
+ #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
+#endif
+#if (!LWIP_NETCONN && LWIP_SOCKET)
+ #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
+ #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK)
+ #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && LWIP_AUTOIP)
+ #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0))
+ #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h"
+#endif
+#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
+ #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
+ #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
+#endif
+/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
+#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT))
+ #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
+#endif
+#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
+ #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
+#endif
+#if (MEM_LIBC_MALLOC && MEM_USE_POOLS)
+ #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h"
+#endif
+#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS)
+ #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h"
+#endif
+#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT)
+ #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf"
+#endif
+#if (TCP_QUEUE_OOSEQ && !LWIP_TCP)
+ #error "TCP_QUEUE_OOSEQ requires LWIP_TCP"
+#endif
+#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
+ #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
+#endif
+#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT
+ #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on"
+#endif
+#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
+ #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
+#endif
+#if LWIP_IGMP && !defined(LWIP_RAND)
+ #error "When using IGMP, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value"
+#endif
+#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
+ #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
+#endif
+#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
+ #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
+#endif
+#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF
+ #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues"
+#endif
+
+
+/* Compile-time checks for deprecated options.
+ */
+#ifdef MEMP_NUM_TCPIP_MSG
+ #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef MEMP_NUM_API_MSG
+ #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef TCP_REXMIT_DEBUG
+ #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef RAW_STATS
+ #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_QUEUE_FIRST
+ #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_ALWAYS_INSERT
+ #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
+#endif
+
+#ifdef LWIP_DEBUG
+static void
+lwip_sanity_check(void)
+{
+ /* Warnings */
+#if LWIP_NETCONN
+ if (MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB))
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN\n"));
+#endif /* LWIP_NETCONN */
+#if LWIP_TCP
+ if (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN\n"));
+ if (TCP_SND_BUF < 2 * TCP_MSS)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly\n"));
+ if (TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF/TCP_MSS)))
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work\n"));
+ if (TCP_SNDLOWAT >= TCP_SND_BUF)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF.\n"));
+ if (TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN.\n"));
+ if (TCP_WND > (PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE))
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE\n"));
+ if (TCP_WND < TCP_MSS)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is smaller than MSS\n"));
+#endif /* LWIP_TCP */
+#if LWIP_SOCKET
+ /* Check that the SO_* socket options and SOF_* lwIP-internal flags match */
+ if (SO_ACCEPTCONN != SOF_ACCEPTCONN)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_ACCEPTCONN != SOF_ACCEPTCONN\n"));
+ if (SO_REUSEADDR != SOF_REUSEADDR)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_REUSEADDR != SOF_REUSEADDR\n"));
+ if (SO_KEEPALIVE != SOF_KEEPALIVE)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_KEEPALIVE != SOF_KEEPALIVE\n"));
+ if (SO_BROADCAST != SOF_BROADCAST)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_BROADCAST != SOF_BROADCAST\n"));
+ if (SO_LINGER != SOF_LINGER)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_LINGER != SOF_LINGER\n"));
+#endif /* LWIP_SOCKET */
+}
+#else /* LWIP_DEBUG */
+#define lwip_sanity_check()
+#endif /* LWIP_DEBUG */
+
+/**
+ * Perform Sanity check of user-configurable values, and initialize all modules.
+ */
+void
+lwip_init(void)
+{
+ /* Sanity check user-configurable values */
+ lwip_sanity_check();
+
+ /* Modules initialization */
+ stats_init();
+#if !NO_SYS
+ sys_init();
+#endif /* !NO_SYS */
+ mem_init();
+ memp_init();
+ pbuf_init();
+ netif_init();
+#if LWIP_SOCKET
+ lwip_socket_init();
+#endif /* LWIP_SOCKET */
+ ip_init();
+#if LWIP_ARP
+ etharp_init();
+#endif /* LWIP_ARP */
+#if LWIP_RAW
+ raw_init();
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ udp_init();
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ tcp_init();
+#endif /* LWIP_TCP */
+#if LWIP_SNMP
+ snmp_init();
+#endif /* LWIP_SNMP */
+#if LWIP_AUTOIP
+ autoip_init();
+#endif /* LWIP_AUTOIP */
+#if LWIP_IGMP
+ igmp_init();
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+ dns_init();
+#endif /* LWIP_DNS */
+
+#if LWIP_TIMERS
+ sys_timeouts_init();
+#endif /* LWIP_TIMERS */
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/ipv4/autoip.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,541 @@
+/**
+ * @file
+ * AutoIP Automatic LinkLocal IP Configuration
+ *
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ *
+ * Please coordinate changes and requests with Dominik Spies
+ * <kontakt@dspies.de>
+ */
+
+/*******************************************************************************
+ * USAGE:
+ *
+ * define LWIP_AUTOIP 1 in your lwipopts.h
+ *
+ * If you don't use tcpip.c (so, don't call, you don't call tcpip_init):
+ * - First, call autoip_init().
+ * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces,
+ * that should be defined in autoip.h.
+ * I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
+ * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ *
+ * Without DHCP:
+ * - Call autoip_start() after netif_add().
+ *
+ * With DHCP:
+ * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
+ * - Configure your DHCP Client.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/autoip.h"
+#include "netif/etharp.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* 169.254.0.0 */
+#define AUTOIP_NET 0xA9FE0000
+/* 169.254.1.0 */
+#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100)
+/* 169.254.254.255 */
+#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF)
+
+
+/** Pseudo random macro based on netif informations.
+ * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
+#ifndef LWIP_AUTOIP_RAND
+#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
+ ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
+ ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
+ ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
+ (netif->autoip?netif->autoip->tried_llipaddr:0))
+#endif /* LWIP_AUTOIP_RAND */
+
+/**
+ * Macro that generates the initial IP address to be tried by AUTOIP.
+ * If you want to override this, define it to something else in lwipopts.h.
+ */
+#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
+#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
+ htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
+ ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
+#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
+
+/* static functions */
+static void autoip_handle_arp_conflict(struct netif *netif);
+
+/* creates a pseudo random LL IP-Address for a network interface */
+static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr);
+
+/* sends an ARP probe */
+static err_t autoip_arp_probe(struct netif *netif);
+
+/* sends an ARP announce */
+static err_t autoip_arp_announce(struct netif *netif);
+
+/* configure interface for use with current LL IP-Address */
+static err_t autoip_bind(struct netif *netif);
+
+/* start sending probes for llipaddr */
+static void autoip_start_probing(struct netif *netif);
+
+/**
+ * Initialize this module
+ */
+void
+autoip_init(void)
+{
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_init()\n"));
+}
+
+/** Set a statically allocated struct autoip to work with.
+ * Using this prevents autoip_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct autoip
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+autoip_set_struct(struct netif *netif, struct autoip *autoip)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("autoip != NULL", autoip != NULL);
+ LWIP_ASSERT("netif already has a struct autoip set", netif->autoip == NULL);
+
+ /* clear data structure */
+ memset(autoip, 0, sizeof(struct autoip));
+ /* autoip->state = AUTOIP_STATE_OFF; */
+ netif->autoip = autoip;
+}
+
+/** Restart AutoIP client and check the next address (conflict detected)
+ *
+ * @param netif The netif under AutoIP control
+ */
+static void
+autoip_restart(struct netif *netif)
+{
+ netif->autoip->tried_llipaddr++;
+ autoip_start(netif);
+}
+
+/**
+ * Handle a IP address conflict after an ARP conflict detection
+ */
+static void
+autoip_handle_arp_conflict(struct netif *netif)
+{
+ /* Somehow detect if we are defending or retreating */
+ unsigned char defend = 1; /* tbd */
+
+ if(defend) {
+ if(netif->autoip->lastconflict > 0) {
+ /* retreat, there was a conflicting ARP in the last
+ * DEFEND_INTERVAL seconds
+ */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
+
+ /* TODO: close all TCP sessions */
+ autoip_restart(netif);
+ } else {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
+ autoip_arp_announce(netif);
+ netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+ } else {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we do not defend, retreating\n"));
+ /* TODO: close all TCP sessions */
+ autoip_restart(netif);
+ }
+}
+
+/**
+ * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ *
+ * @param netif network interface on which create the IP-Address
+ * @param ipaddr ip address to initialize
+ */
+static void
+autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr)
+{
+ /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ * compliant to RFC 3927 Section 2.1
+ * We have 254 * 256 possibilities */
+
+ u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
+ addr += netif->autoip->tried_llipaddr;
+ addr = AUTOIP_NET | (addr & 0xffff);
+ /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */
+
+ if (addr < AUTOIP_RANGE_START) {
+ addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+ }
+ if (addr > AUTOIP_RANGE_END) {
+ addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+ }
+ LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
+ (addr <= AUTOIP_RANGE_END));
+ ip4_addr_set_u32(ipaddr, htonl(addr));
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (u16_t)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
+ ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+}
+
+/**
+ * Sends an ARP probe from a network interface
+ *
+ * @param netif network interface used to send the probe
+ */
+static err_t
+autoip_arp_probe(struct netif *netif)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
+ (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, ðzero,
+ &netif->autoip->llipaddr, ARP_REQUEST);
+}
+
+/**
+ * Sends an ARP announce from a network interface
+ *
+ * @param netif network interface used to send the announce
+ */
+static err_t
+autoip_arp_announce(struct netif *netif)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
+ (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero,
+ &netif->autoip->llipaddr, ARP_REQUEST);
+}
+
+/**
+ * Configure interface for use with current LL IP-Address
+ *
+ * @param netif network interface to configure with current LL IP-Address
+ */
+static err_t
+autoip_bind(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+ ip_addr_t sn_mask, gw_addr;
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+
+ IP4_ADDR(&sn_mask, 255, 255, 0, 0);
+ IP4_ADDR(&gw_addr, 0, 0, 0, 0);
+
+ netif_set_ipaddr(netif, &autoip->llipaddr);
+ netif_set_netmask(netif, &sn_mask);
+ netif_set_gw(netif, &gw_addr);
+
+ /* bring the interface up */
+ netif_set_up(netif);
+
+ return ERR_OK;
+}
+
+/**
+ * Start AutoIP client
+ *
+ * @param netif network interface on which start the AutoIP client
+ */
+err_t
+autoip_start(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+ err_t result = ERR_OK;
+
+ if(netif_is_up(netif)) {
+ netif_set_down(netif);
+ }
+
+ /* Set IP-Address, Netmask and Gateway to 0 to make sure that
+ * ARP Packets are formed correctly
+ */
+ ip_addr_set_zero(&netif->ip_addr);
+ ip_addr_set_zero(&netif->netmask);
+ ip_addr_set_zero(&netif->gw);
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0],
+ netif->name[1], (u16_t)netif->num));
+ if(autoip == NULL) {
+ /* no AutoIP client attached yet? */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_start(): starting new AUTOIP client\n"));
+ autoip = (struct autoip *)mem_malloc(sizeof(struct autoip));
+ if(autoip == NULL) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_start(): could not allocate autoip\n"));
+ return ERR_MEM;
+ }
+ memset(autoip, 0, sizeof(struct autoip));
+ /* store this AutoIP client in the netif */
+ netif->autoip = autoip;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
+ } else {
+ autoip->state = AUTOIP_STATE_OFF;
+ autoip->ttw = 0;
+ autoip->sent_num = 0;
+ ip_addr_set_zero(&autoip->llipaddr);
+ autoip->lastconflict = 0;
+ }
+
+ autoip_create_addr(netif, &(autoip->llipaddr));
+ autoip_start_probing(netif);
+
+ return result;
+}
+
+static void
+autoip_start_probing(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+
+ autoip->state = AUTOIP_STATE_PROBING;
+ autoip->sent_num = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+
+ /* time to wait to first probe, this is randomly
+ * choosen out of 0 to PROBE_WAIT seconds.
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
+
+ /*
+ * if we tried more then MAX_CONFLICTS we must limit our rate for
+ * accquiring and probing address
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ if(autoip->tried_llipaddr > MAX_CONFLICTS) {
+ autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+}
+
+/**
+ * Handle a possible change in the network configuration.
+ *
+ * If there is an AutoIP address configured, take the interface down
+ * and begin probing with the same address.
+ */
+void
+autoip_network_changed(struct netif *netif)
+{
+ if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) {
+ netif_set_down(netif);
+ autoip_start_probing(netif);
+ }
+}
+
+/**
+ * Stop AutoIP client
+ *
+ * @param netif network interface on which stop the AutoIP client
+ */
+err_t
+autoip_stop(struct netif *netif)
+{
+ netif->autoip->state = AUTOIP_STATE_OFF;
+ netif_set_down(netif);
+ return ERR_OK;
+}
+
+/**
+ * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
+ */
+void
+autoip_tmr()
+{
+ struct netif *netif = netif_list;
+ /* loop through netif's */
+ while (netif != NULL) {
+ /* only act on AutoIP configured interfaces */
+ if (netif->autoip != NULL) {
+ if(netif->autoip->lastconflict > 0) {
+ netif->autoip->lastconflict--;
+ }
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
+ (u16_t)(netif->autoip->state), netif->autoip->ttw));
+
+ switch(netif->autoip->state) {
+ case AUTOIP_STATE_PROBING:
+ if(netif->autoip->ttw > 0) {
+ netif->autoip->ttw--;
+ } else {
+ if(netif->autoip->sent_num >= PROBE_NUM) {
+ netif->autoip->state = AUTOIP_STATE_ANNOUNCING;
+ netif->autoip->sent_num = 0;
+ netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ } else {
+ autoip_arp_probe(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() PROBING Sent Probe\n"));
+ netif->autoip->sent_num++;
+ /* calculate time to wait to next probe */
+ netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
+ ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
+ PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
+ }
+ }
+ break;
+
+ case AUTOIP_STATE_ANNOUNCING:
+ if(netif->autoip->ttw > 0) {
+ netif->autoip->ttw--;
+ } else {
+ if(netif->autoip->sent_num == 0) {
+ /* We are here the first time, so we waited ANNOUNCE_WAIT seconds
+ * Now we can bind to an IP address and use it.
+ *
+ * autoip_bind calls netif_set_up. This triggers a gratuitous ARP
+ * which counts as an announcement.
+ */
+ autoip_bind(netif);
+ } else {
+ autoip_arp_announce(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() ANNOUNCING Sent Announce\n"));
+ }
+ netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ netif->autoip->sent_num++;
+
+ if(netif->autoip->sent_num >= ANNOUNCE_NUM) {
+ netif->autoip->state = AUTOIP_STATE_BOUND;
+ netif->autoip->sent_num = 0;
+ netif->autoip->ttw = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ }
+ }
+ break;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+
+/**
+ * Handles every incoming ARP Packet, called by etharp_arp_input.
+ *
+ * @param netif network interface to use for autoip processing
+ * @param hdr Incoming ARP packet
+ */
+void
+autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
+{
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
+ if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) {
+ /* when ip.src == llipaddr && hw.src != netif->hwaddr
+ *
+ * when probing ip.dst == llipaddr && hw.src != netif->hwaddr
+ * we have a conflict and must solve it
+ */
+ ip_addr_t sipaddr, dipaddr;
+ struct eth_addr netifaddr;
+ netifaddr.addr[0] = netif->hwaddr[0];
+ netifaddr.addr[1] = netif->hwaddr[1];
+ netifaddr.addr[2] = netif->hwaddr[2];
+ netifaddr.addr[3] = netif->hwaddr[3];
+ netifaddr.addr[4] = netif->hwaddr[4];
+ netifaddr.addr[5] = netif->hwaddr[5];
+
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules).
+ */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ if ((netif->autoip->state == AUTOIP_STATE_PROBING) ||
+ ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) &&
+ (netif->autoip->sent_num == 0))) {
+ /* RFC 3927 Section 2.2.1:
+ * from beginning to after ANNOUNCE_WAIT
+ * seconds we have a conflict if
+ * ip.src == llipaddr OR
+ * ip.dst == llipaddr && hw.src != own hwaddr
+ */
+ if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) ||
+ (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Probe Conflict detected\n"));
+ autoip_restart(netif);
+ }
+ } else {
+ /* RFC 3927 Section 2.5:
+ * in any state we have a conflict if
+ * ip.src == llipaddr && hw.src != own hwaddr
+ */
+ if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
+ autoip_handle_arp_conflict(netif);
+ }
+ }
+ }
+}
+
+#endif /* LWIP_AUTOIP */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/ipv4/icmp.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,335 @@
+/**
+ * @file
+ * ICMP - Internet Control Message Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* Some ICMP messages should be passed to the transport protocols. This
+ is not implemented. */
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
+ * used to modify and send a response packet (and to 1 if this is not the case,
+ * e.g. when link header is stripped of when receiving) */
+#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+
+/* The amount of data from the original packet to return in a dest-unreachable */
+#define ICMP_DEST_UNREACH_DATASIZE 8
+
+static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
+
+/**
+ * Processes ICMP input packets, called from ip_input().
+ *
+ * Currently only processes icmp echo requests and sends
+ * out the echo response.
+ *
+ * @param p the icmp echo request packet, p->payload pointing to the ip header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+ u8_t type;
+#ifdef LWIP_DEBUG
+ u8_t code;
+#endif /* LWIP_DEBUG */
+ struct icmp_echo_hdr *iecho;
+ struct ip_hdr *iphdr;
+ s16_t hlen;
+
+ ICMP_STATS_INC(icmp.recv);
+ snmp_inc_icmpinmsgs();
+
+
+ iphdr = (struct ip_hdr *)p->payload;
+ hlen = IPH_HL(iphdr) * 4;
+ if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
+ goto lenerr;
+ }
+
+ type = *((u8_t *)p->payload);
+#ifdef LWIP_DEBUG
+ code = *(((u8_t *)p->payload)+1);
+#endif /* LWIP_DEBUG */
+ switch (type) {
+ case ICMP_ER:
+ /* This is OK, echo reply might have been parsed by a raw PCB
+ (as obviously, an echo request has been sent, too). */
+ break;
+ case ICMP_ECHO:
+#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
+ {
+ int accepted = 1;
+#if !LWIP_MULTICAST_PING
+ /* multicast destination address? */
+ if (ip_addr_ismulticast(¤t_iphdr_dest)) {
+ accepted = 0;
+ }
+#endif /* LWIP_MULTICAST_PING */
+#if !LWIP_BROADCAST_PING
+ /* broadcast destination address? */
+ if (ip_addr_isbroadcast(¤t_iphdr_dest, inp)) {
+ accepted = 0;
+ }
+#endif /* LWIP_BROADCAST_PING */
+ /* broadcast or multicast destination address not acceptd? */
+ if (!accepted) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
+ ICMP_STATS_INC(icmp.err);
+ pbuf_free(p);
+ return;
+ }
+ }
+#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
+ if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
+ goto lenerr;
+ }
+ if (inet_chksum_pbuf(p) != 0) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.chkerr);
+ snmp_inc_icmpinerrors();
+ return;
+ }
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+ if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
+ /* p is not big enough to contain link headers
+ * allocate a new one and copy p into it
+ */
+ struct pbuf *r;
+ /* switch p->payload to ip header */
+ if (pbuf_header(p, hlen)) {
+ LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0);
+ goto memerr;
+ }
+ /* allocate new packet buffer with space for link headers */
+ r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
+ goto memerr;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct the ICMP header",
+ (r->len >= hlen + sizeof(struct icmp_echo_hdr)));
+ /* copy the whole packet including ip header */
+ if (pbuf_copy(r, p) != ERR_OK) {
+ LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0);
+ goto memerr;
+ }
+ iphdr = (struct ip_hdr *)r->payload;
+ /* switch r->payload back to icmp header */
+ if (pbuf_header(r, -hlen)) {
+ LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+ goto memerr;
+ }
+ /* free the original p */
+ pbuf_free(p);
+ /* we now have an identical copy of p that has room for link headers */
+ p = r;
+ } else {
+ /* restore p->payload to point to icmp header */
+ if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
+ LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+ goto memerr;
+ }
+ }
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+ /* At this point, all checks are OK. */
+ /* We generate an answer by switching the dest and src ip addresses,
+ * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
+ iecho = (struct icmp_echo_hdr *)p->payload;
+ ip_addr_copy(iphdr->src, *ip_current_dest_addr());
+ ip_addr_copy(iphdr->dest, *ip_current_src_addr());
+ ICMPH_TYPE_SET(iecho, ICMP_ER);
+ /* adjust the checksum */
+ if (iecho->chksum >= PP_HTONS(0xffff - (ICMP_ECHO << 8))) {
+ iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;
+ } else {
+ iecho->chksum += PP_HTONS(ICMP_ECHO << 8);
+ }
+
+ /* Set the correct TTL and recalculate the header checksum. */
+ IPH_TTL_SET(iphdr, ICMP_TTL);
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+#endif /* CHECKSUM_GEN_IP */
+
+ ICMP_STATS_INC(icmp.xmit);
+ /* increase number of messages attempted to send */
+ snmp_inc_icmpoutmsgs();
+ /* increase number of echo replies attempted to send */
+ snmp_inc_icmpoutechoreps();
+
+ if(pbuf_header(p, hlen)) {
+ LWIP_ASSERT("Can't move over header in packet", 0);
+ } else {
+ err_t ret;
+ /* send an ICMP packet, src addr is the dest addr of the curren packet */
+ ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL,
+ ICMP_TTL, 0, IP_PROTO_ICMP, inp);
+ if (ret != ERR_OK) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret));
+ }
+ }
+ break;
+ default:
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
+ (s16_t)type, (s16_t)code));
+ ICMP_STATS_INC(icmp.proterr);
+ ICMP_STATS_INC(icmp.drop);
+ }
+ pbuf_free(p);
+ return;
+lenerr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.lenerr);
+ snmp_inc_icmpinerrors();
+ return;
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+memerr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.err);
+ snmp_inc_icmpinerrors();
+ return;
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+}
+
+/**
+ * Send an icmp 'destination unreachable' packet, called from ip_input() if
+ * the transport layer protocol is unknown and from udp_input() if the local
+ * port is not bound.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'unreachable' packet
+ */
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+ icmp_send_response(p, ICMP_DUR, t);
+}
+
+#if IP_FORWARD || IP_REASSEMBLY
+/**
+ * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'time exceeded' packet
+ */
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+ icmp_send_response(p, ICMP_TE, t);
+}
+
+#endif /* IP_FORWARD || IP_REASSEMBLY */
+
+/**
+ * Send an icmp packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param type Type of the ICMP header
+ * @param code Code of the ICMP header
+ */
+static void
+icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
+{
+ struct pbuf *q;
+ struct ip_hdr *iphdr;
+ /* we can use the echo header here */
+ struct icmp_echo_hdr *icmphdr;
+ ip_addr_t iphdr_src;
+
+ /* ICMP header + IP header + 8 bytes of data */
+ q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
+ PBUF_RAM);
+ if (q == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold icmp message",
+ (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
+ ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
+ LWIP_DEBUGF(ICMP_DEBUG, (" to "));
+ ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
+ LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
+
+ icmphdr = (struct icmp_echo_hdr *)q->payload;
+ icmphdr->type = type;
+ icmphdr->code = code;
+ icmphdr->id = 0;
+ icmphdr->seqno = 0;
+
+ /* copy fields from original packet */
+ SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
+ IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
+
+ /* calculate checksum */
+ icmphdr->chksum = 0;
+ icmphdr->chksum = inet_chksum(icmphdr, q->len);
+ ICMP_STATS_INC(icmp.xmit);
+ /* increase number of messages attempted to send */
+ snmp_inc_icmpoutmsgs();
+ /* increase number of destination unreachable messages attempted to send */
+ snmp_inc_icmpouttimeexcds();
+ ip_addr_copy(iphdr_src, iphdr->src);
+ ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP);
+ pbuf_free(q);
+}
+
+#endif /* LWIP_ICMP */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/ipv4/igmp.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,818 @@
+#pragma diag_remark 177
+/**
+ * @file
+ * IGMP - Internet Group Management Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+/*-------------------------------------------------------------
+Note 1)
+Although the rfc requires V1 AND V2 capability
+we will only support v2 since now V1 is very old (August 1989)
+V1 can be added if required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 2)
+A query for a specific group address (as opposed to ALLHOSTS)
+has now been implemented as I am unsure if it is required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 3)
+The router alert rfc 2113 is implemented in outgoing packets
+but not checked rigorously incoming
+-------------------------------------------------------------
+Steve Reynolds
+------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * RFC 988 - Host extensions for IP multicasting - V0
+ * RFC 1054 - Host extensions for IP multicasting -
+ * RFC 1112 - Host extensions for IP multicasting - V1
+ * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard)
+ * RFC 3376 - Internet Group Management Protocol, Version 3 - V3
+ * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+
+ * RFC 2113 - IP Router Alert Option -
+ *----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/igmp.h"
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/stats.h"
+
+#include "string.h"
+
+/*
+ * IGMP constants
+ */
+#define IGMP_TTL 1
+#define IGMP_MINLEN 8
+#define ROUTER_ALERT 0x9404
+#define ROUTER_ALERTLEN 4
+
+/*
+ * IGMP message types, including version number.
+ */
+#define IGMP_MEMB_QUERY 0x11 /* Membership query */
+#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */
+#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */
+#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */
+
+/* Group membership states */
+#define IGMP_GROUP_NON_MEMBER 0
+#define IGMP_GROUP_DELAYING_MEMBER 1
+#define IGMP_GROUP_IDLE_MEMBER 2
+
+/**
+ * IGMP packet format.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct igmp_msg {
+ PACK_STRUCT_FIELD(u8_t igmp_msgtype);
+ PACK_STRUCT_FIELD(u8_t igmp_maxresp);
+ PACK_STRUCT_FIELD(u16_t igmp_checksum);
+ PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+
+static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
+static err_t igmp_remove_group(struct igmp_group *group);
+static void igmp_timeout( struct igmp_group *group);
+static void igmp_start_timer(struct igmp_group *group, u8_t max_time);
+static void igmp_stop_timer(struct igmp_group *group);
+static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
+static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
+static void igmp_send(struct igmp_group *group, u8_t type);
+
+
+static struct igmp_group* igmp_group_list;
+static ip_addr_t allsystems;
+static ip_addr_t allrouters;
+
+
+/**
+ * Initialize the IGMP module
+ */
+void
+igmp_init(void)
+{
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
+
+ IP4_ADDR(&allsystems, 224, 0, 0, 1);
+ IP4_ADDR(&allrouters, 224, 0, 0, 2);
+}
+
+#ifdef LWIP_DEBUG
+/**
+ * Dump global IGMP groups list
+ */
+void
+igmp_dump_group_list()
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
+ ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
+ group = group->next;
+ }
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+}
+#else
+#define igmp_dump_group_list()
+#endif /* LWIP_DEBUG */
+
+/**
+ * Start IGMP processing on interface
+ *
+ * @param netif network interface on which start IGMP processing
+ */
+err_t
+igmp_start(struct netif *netif)
+{
+ struct igmp_group* group;
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
+
+ group = igmp_lookup_group(netif, &allsystems);
+
+ if (group != NULL) {
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->use++;
+
+ /* Allow the igmp messages at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
+ ip_addr_debug_print(IGMP_DEBUG, &allsystems);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
+ }
+
+ return ERR_OK;
+ }
+
+ return ERR_MEM;
+}
+
+/**
+ * Stop IGMP processing on interface
+ *
+ * @param netif network interface on which stop IGMP processing
+ */
+err_t
+igmp_stop(struct netif *netif)
+{
+ struct igmp_group *group = igmp_group_list;
+ struct igmp_group *prev = NULL;
+ struct igmp_group *next;
+
+ /* look for groups joined on this interface further down the list */
+ while (group != NULL) {
+ next = group->next;
+ /* is it a group joined on this interface? */
+ if (group->netif == netif) {
+ /* is it the first group of the list? */
+ if (group == igmp_group_list) {
+ igmp_group_list = next;
+ }
+ /* is there a "previous" group defined? */
+ if (prev != NULL) {
+ prev->next = next;
+ }
+ /* disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
+ ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
+ }
+ /* free group */
+ memp_free(MEMP_IGMP_GROUP, group);
+ } else {
+ /* change the "previous" */
+ prev = group;
+ }
+ /* move to "next" */
+ group = next;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Report IGMP memberships for this interface
+ *
+ * @param netif network interface on which report IGMP memberships
+ */
+void
+igmp_report_groups(struct netif *netif)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
+
+ while (group != NULL) {
+ if (group->netif == netif) {
+ igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+ }
+ group = group->next;
+ }
+}
+
+/**
+ * Search for a group in the global igmp_group_list
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search for
+ * @return a struct igmp_group* if the group has been found,
+ * NULL if the group wasn't found.
+ */
+struct igmp_group *
+igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
+ return group;
+ }
+ group = group->next;
+ }
+
+ /* to be clearer, we return NULL here instead of
+ * 'group' (which is also NULL at this point).
+ */
+ return NULL;
+}
+
+/**
+ * Search for a specific igmp group and create a new one if not found-
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search
+ * @return a struct igmp_group*,
+ * NULL on memory error.
+ */
+struct igmp_group *
+igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ /* Search if the group already exists */
+ group = igmp_lookfor_group(ifp, addr);
+ if (group != NULL) {
+ /* Group already exists. */
+ return group;
+ }
+
+ /* Group doesn't exist yet, create a new one */
+ group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
+ if (group != NULL) {
+ group->netif = ifp;
+ ip_addr_set(&(group->group_address), addr);
+ group->timer = 0; /* Not running */
+ group->group_state = IGMP_GROUP_NON_MEMBER;
+ group->last_reporter_flag = 0;
+ group->use = 0;
+ group->next = igmp_group_list;
+
+ igmp_group_list = group;
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
+ ip_addr_debug_print(IGMP_DEBUG, addr);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
+
+ return group;
+}
+
+/**
+ * Remove a group in the global igmp_group_list
+ *
+ * @param group the group to remove from the global igmp_group_list
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+igmp_remove_group(struct igmp_group *group)
+{
+ err_t err = ERR_OK;
+
+ /* Is it the first group? */
+ if (igmp_group_list == group) {
+ igmp_group_list = group->next;
+ } else {
+ /* look for group further down the list */
+ struct igmp_group *tmpGroup;
+ for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
+ if (tmpGroup->next == group) {
+ tmpGroup->next = group->next;
+ break;
+ }
+ }
+ /* Group not found in the global igmp_group_list */
+ if (tmpGroup == NULL)
+ err = ERR_ARG;
+ }
+ /* free group */
+ memp_free(MEMP_IGMP_GROUP, group);
+
+ return err;
+}
+
+/**
+ * Called from ip_input() if a new IGMP packet is received.
+ *
+ * @param p received igmp packet, p->payload pointing to the ip header
+ * @param inp network interface on which the packet was received
+ * @param dest destination ip address of the igmp packet
+ */
+void
+igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
+{
+ struct ip_hdr * iphdr;
+ struct igmp_msg* igmp;
+ struct igmp_group* group;
+ struct igmp_group* groupref;
+
+ IGMP_STATS_INC(igmp.recv);
+
+ /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
+ iphdr = (struct ip_hdr *)p->payload;
+ if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.lenerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
+ return;
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
+ ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
+ LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
+ ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
+
+ /* Now calculate and check the checksum */
+ igmp = (struct igmp_msg *)p->payload;
+ if (inet_chksum(igmp, p->len)) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.chkerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
+ return;
+ }
+
+ /* Packet is ok so find an existing group */
+ group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
+
+ /* If group can be found or create... */
+ if (!group) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.drop);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
+ return;
+ }
+
+ /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
+ switch (igmp->igmp_msgtype) {
+ case IGMP_MEMB_QUERY: {
+ /* IGMP_MEMB_QUERY to the "all systems" address ? */
+ if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
+ /* THIS IS THE GENERAL QUERY */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+
+ if (igmp->igmp_maxresp == 0) {
+ IGMP_STATS_INC(igmp.rx_v1);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
+ igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
+ } else {
+ IGMP_STATS_INC(igmp.rx_general);
+ }
+
+ groupref = igmp_group_list;
+ while (groupref) {
+ /* Do not send messages on the all systems group address! */
+ if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
+ igmp_delaying_member(groupref, igmp->igmp_maxresp);
+ }
+ groupref = groupref->next;
+ }
+ } else {
+ /* IGMP_MEMB_QUERY to a specific group ? */
+ if (!ip_addr_isany(&igmp->igmp_group_address)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
+ ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
+ if (ip_addr_cmp(dest, &allsystems)) {
+ ip_addr_t groupaddr;
+ LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ /* we first need to re-look for the group since we used dest last time */
+ ip_addr_copy(groupaddr, igmp->igmp_group_address);
+ group = igmp_lookfor_group(inp, &groupaddr);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ }
+
+ if (group != NULL) {
+ IGMP_STATS_INC(igmp.rx_group);
+ igmp_delaying_member(group, igmp->igmp_maxresp);
+ } else {
+ IGMP_STATS_INC(igmp.drop);
+ }
+ } else {
+ IGMP_STATS_INC(igmp.proterr);
+ }
+ }
+ break;
+ }
+ case IGMP_V2_MEMB_REPORT: {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
+ IGMP_STATS_INC(igmp.rx_report);
+ if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ /* This is on a specific group we have already looked up */
+ group->timer = 0; /* stopped */
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->last_reporter_flag = 0;
+ }
+ break;
+ }
+ default: {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
+ igmp->igmp_msgtype, group->group_state, &group, group->netif));
+ IGMP_STATS_INC(igmp.proterr);
+ break;
+ }
+ }
+
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * Join a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct igmp_group *group;
+ struct netif *netif;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we join this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
+ /* find group or create a new one if not found */
+ group = igmp_lookup_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* This should create a new group, check the state to make sure */
+ if (group->group_state != IGMP_GROUP_NON_MEMBER) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
+ } else {
+ /* OK - it was new group */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If first use of the group, allow the group at the MAC level */
+ if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
+ }
+
+ IGMP_STATS_INC(igmp.tx_join);
+ igmp_send(group, IGMP_V2_MEMB_REPORT);
+
+ igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+
+ /* Need to work out where this timer comes from */
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+ /* Increment group use */
+ group->use++;
+ /* Join on this interface */
+ err = ERR_OK;
+ } else {
+ /* Return an error even if some network interfaces are joined */
+ /** @todo undo any other netif already joined */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
+ return ERR_MEM;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * Leave a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct igmp_group *group;
+ struct netif *netif;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we leave this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
+ /* find group */
+ group = igmp_lookfor_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* Only send a leave if the flag is set according to the state diagram */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If there is no other use of the group */
+ if (group->use <= 1) {
+ /* If we are the last reporter for this group */
+ if (group->last_reporter_flag) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
+ IGMP_STATS_INC(igmp.tx_leave);
+ igmp_send(group, IGMP_LEAVE_GROUP);
+ }
+
+ /* Disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* Free the group */
+ igmp_remove_group(group);
+ } else {
+ /* Decrement group use */
+ group->use--;
+ }
+ /* Leave on this interface */
+ err = ERR_OK;
+ } else {
+ /* It's not a fatal error on "leavegroup" */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * The igmp timer function (both for NO_SYS=1 and =0)
+ * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
+ */
+void
+igmp_tmr(void)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ if (group->timer > 0) {
+ group->timer--;
+ if (group->timer == 0) {
+ igmp_timeout(group);
+ }
+ }
+ group = group->next;
+ }
+}
+
+/**
+ * Called if a timeout for one group is reached.
+ * Sends a report for this group.
+ *
+ * @param group an igmp_group for which a timeout is reached
+ */
+static void
+igmp_timeout(struct igmp_group *group)
+{
+ /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
+ if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
+ ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
+
+ IGMP_STATS_INC(igmp.tx_report);
+ igmp_send(group, IGMP_V2_MEMB_REPORT);
+ }
+}
+
+/**
+ * Start a timer for an igmp group
+ *
+ * @param group the igmp_group for which to start a timer
+ * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
+ * every call to igmp_tmr())
+ */
+static void
+igmp_start_timer(struct igmp_group *group, u8_t max_time)
+{
+ /* ensure the input value is > 0 */
+ if (max_time == 0) {
+ max_time = 1;
+ }
+ /* ensure the random value is > 0 */
+ group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
+}
+
+/**
+ * Stop a timer for an igmp_group
+ *
+ * @param group the igmp_group for which to stop the timer
+ */
+static void
+igmp_stop_timer(struct igmp_group *group)
+{
+ group->timer = 0;
+}
+
+/**
+ * Delaying membership report for a group if necessary
+ *
+ * @param group the igmp_group for which "delaying" membership report
+ * @param maxresp query delay
+ */
+static void
+igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
+{
+ if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
+ ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+ ((group->timer == 0) || (maxresp < group->timer)))) {
+ igmp_start_timer(group, maxresp);
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+}
+
+
+/**
+ * Sends an IP packet on a network interface. This function constructs the IP header
+ * and calculates the IP header checksum. If the source IP address is NULL,
+ * the IP address of the outgoing network interface is filled in as source address.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ */
+static err_t
+igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
+{
+ /* This is the "router alert" option */
+ u16_t ra[2];
+ ra[0] = PP_HTONS(ROUTER_ALERT);
+ ra[1] = 0x0000; /* Router shall examine packet */
+ IGMP_STATS_INC(igmp.xmit);
+ return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
+}
+
+/**
+ * Send an igmp packet to a specific group.
+ *
+ * @param group the group to which to send the packet
+ * @param type the type of igmp packet to send
+ */
+static void
+igmp_send(struct igmp_group *group, u8_t type)
+{
+ struct pbuf* p = NULL;
+ struct igmp_msg* igmp = NULL;
+ ip_addr_t src = *IP_ADDR_ANY;
+ ip_addr_t* dest = NULL;
+
+ /* IP header + "router alert" option + IGMP header */
+ p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
+
+ if (p) {
+ igmp = (struct igmp_msg *)p->payload;
+ LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
+ (p->len >= sizeof(struct igmp_msg)));
+ ip_addr_copy(src, group->netif->ip_addr);
+
+ if (type == IGMP_V2_MEMB_REPORT) {
+ dest = &(group->group_address);
+ ip_addr_copy(igmp->igmp_group_address, group->group_address);
+ group->last_reporter_flag = 1; /* Remember we were the last to report */
+ } else {
+ if (type == IGMP_LEAVE_GROUP) {
+ dest = &allrouters;
+ ip_addr_copy(igmp->igmp_group_address, group->group_address);
+ }
+ }
+
+ if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
+ igmp->igmp_msgtype = type;
+ igmp->igmp_maxresp = 0;
+ igmp->igmp_checksum = 0;
+ igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
+
+ igmp_ip_output_if(p, &src, dest, group->netif);
+ }
+
+ pbuf_free(p);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
+ IGMP_STATS_INC(igmp.memerr);
+ }
+}
+
+#endif /* LWIP_IGMP */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lwip/core/ipv4/inet.c Thu Sep 08 10:48:09 2011 +0000 @@ -0,0 +1,42 @@ +/** + * @file + * Functions common to all TCP/IPv4 modules, such as the byte order functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels <adam@sics.se> + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet.h" +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/ipv4/inet_chksum.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,450 @@
+/**
+ * @file
+ * Incluse internet checksum functions.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/inet_chksum.h"
+#include "lwip/def.h"
+
+#include <stddef.h>
+#include <string.h>
+
+/* These are some reference implementations of the checksum algorithm, with the
+ * aim of being simple, correct and fully portable. Checksumming is the
+ * first thing you would want to optimize for your platform. If you create
+ * your own version, link it in and in your cc.h put:
+ *
+ * #define LWIP_CHKSUM <your_checksum_routine>
+ *
+ * Or you can select from the implementations below by defining
+ * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
+ */
+
+#ifndef LWIP_CHKSUM
+# define LWIP_CHKSUM lwip_standard_chksum
+# ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 2
+# endif
+#endif
+/* If none set: */
+#ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 0
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
+/**
+ * lwip checksum
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * @note accumulator size limits summable length to 64k
+ * @note host endianess is irrelevant (p3 RFC1071)
+ */
+static u16_t
+lwip_standard_chksum(void *dataptr, u16_t len)
+{
+ u32_t acc;
+ u16_t src;
+ u8_t *octetptr;
+
+ acc = 0;
+ /* dataptr may be at odd or even addresses */
+ octetptr = (u8_t*)dataptr;
+ while (len > 1) {
+ /* declare first octet as most significant
+ thus assume network order, ignoring host order */
+ src = (*octetptr) << 8;
+ octetptr++;
+ /* declare second octet as least significant */
+ src |= (*octetptr);
+ octetptr++;
+ acc += src;
+ len -= 2;
+ }
+ if (len > 0) {
+ /* accumulate remaining octet */
+ src = (*octetptr) << 8;
+ acc += src;
+ }
+ /* add deferred carry bits */
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ if ((acc & 0xffff0000UL) != 0) {
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ }
+ /* This maybe a little confusing: reorder sum using htons()
+ instead of ntohs() since it has a little less call overhead.
+ The caller must invert bits for Internet sum ! */
+ return htons((u16_t)acc);
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
+/*
+ * Curt McDowell
+ * Broadcom Corp.
+ * csm@broadcom.com
+ *
+ * IP checksum two bytes at a time with support for
+ * unaligned buffer.
+ * Works for len up to and including 0x20000.
+ * by Curt McDowell, Broadcom Corp. 12/08/2005
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ */
+
+static u16_t
+lwip_standard_chksum(void *dataptr, int len)
+{
+ u8_t *pb = (u8_t *)dataptr;
+ u16_t *ps, t = 0;
+ u32_t sum = 0;
+ int odd = ((mem_ptr_t)pb & 1);
+
+ /* Get aligned to u16_t */
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ /* Add the bulk of the data */
+ ps = (u16_t *)(void *)pb;
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* Consume left-over byte, if any */
+ if (len > 0) {
+ ((u8_t *)&t)[0] = *(u8_t *)ps;
+ }
+
+ /* Add end bytes */
+ sum += t;
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ /* Swap if alignment was odd */
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
+/**
+ * An optimized checksum routine. Basically, it uses loop-unrolling on
+ * the checksum loop, treating the head and tail bytes specially, whereas
+ * the inner loop acts on 8 bytes at a time.
+ *
+ * @arg start of buffer to be checksummed. May be an odd byte address.
+ * @len number of bytes in the buffer to be checksummed.
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * by Curt McDowell, Broadcom Corp. December 8th, 2005
+ */
+
+static u16_t
+lwip_standard_chksum(void *dataptr, int len)
+{
+ u8_t *pb = (u8_t *)dataptr;
+ u16_t *ps, t = 0;
+ u32_t *pl;
+ u32_t sum = 0, tmp;
+ /* starts at odd byte address? */
+ int odd = ((mem_ptr_t)pb & 1);
+
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ ps = (u16_t *)pb;
+
+ if (((mem_ptr_t)ps & 3) && len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ pl = (u32_t *)ps;
+
+ while (len > 7) {
+ tmp = sum + *pl++; /* ping */
+ if (tmp < sum) {
+ tmp++; /* add back carry */
+ }
+
+ sum = tmp + *pl++; /* pong */
+ if (sum < tmp) {
+ sum++; /* add back carry */
+ }
+
+ len -= 8;
+ }
+
+ /* make room in upper bits */
+ sum = FOLD_U32T(sum);
+
+ ps = (u16_t *)pl;
+
+ /* 16-bit aligned word remaining? */
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* dangling tail byte remaining? */
+ if (len > 0) { /* include odd byte */
+ ((u8_t *)&t)[0] = *(u8_t *)ps;
+ }
+
+ sum += t; /* add end bytes */
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+/* inet_chksum_pseudo:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo(struct pbuf *p,
+ ip_addr_t *src, ip_addr_t *dest,
+ u8_t proto, u16_t proto_len)
+{
+ u32_t acc;
+ u32_t addr;
+ struct pbuf *q;
+ u8_t swapped;
+
+ acc = 0;
+ swapped = 0;
+ /* iterate through all pbuf in chain */
+ for(q = p; q != NULL; q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* just executing this next line is probably faster that the if statement needed
+ to check whether we really need to execute it, and does no harm */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ addr = ip4_addr_get_u32(src);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = ip4_addr_get_u32(dest);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ acc += (u32_t)htons((u16_t)proto);
+ acc += (u32_t)htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* inet_chksum_pseudo:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo_partial(struct pbuf *p,
+ ip_addr_t *src, ip_addr_t *dest,
+ u8_t proto, u16_t proto_len, u16_t chksum_len)
+{
+ u32_t acc;
+ u32_t addr;
+ struct pbuf *q;
+ u8_t swapped;
+ u16_t chklen;
+
+ acc = 0;
+ swapped = 0;
+ /* iterate through all pbuf in chain */
+ for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ chklen = q->len;
+ if (chklen > chksum_len) {
+ chklen = chksum_len;
+ }
+ acc += LWIP_CHKSUM(q->payload, chklen);
+ chksum_len -= chklen;
+ LWIP_ASSERT("delete me", chksum_len < 0x7fff);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* fold the upper bit down */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ addr = ip4_addr_get_u32(src);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = ip4_addr_get_u32(dest);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ acc += (u32_t)htons((u16_t)proto);
+ acc += (u32_t)htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarily for IP
+ * and ICMP.
+ *
+ * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
+ * @param len length of the buffer to calculate the checksum
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+
+u16_t
+inet_chksum(void *dataptr, u16_t len)
+{
+ return ~LWIP_CHKSUM(dataptr, len);
+}
+
+/**
+ * Calculate a checksum over a chain of pbufs (without pseudo-header, much like
+ * inet_chksum only pbufs are used).
+ *
+ * @param p pbuf chain over that the checksum should be calculated
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+ u32_t acc;
+ struct pbuf *q;
+ u8_t swapped;
+
+ acc = 0;
+ swapped = 0;
+ for(q = p; q != NULL; q = q->next) {
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
+ * like MEMCPY but generates a checksum at the same time. Since this is a
+ * performance-sensitive function, you might want to create your own version
+ * in assembly targeted at your hardware by defining it in lwipopts.h:
+ * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
+ */
+
+#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
+/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
+ * For architectures with big caches, data might still be in cache when
+ * generating the checksum after copying.
+ */
+u16_t
+lwip_chksum_copy(void *dst, const void *src, u16_t len)
+{
+ MEMCPY(dst, src, len);
+ return LWIP_CHKSUM(dst, len);
+}
+#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/ipv4/ip.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,857 @@
+/**
+ * @file
+ * This is the IPv4 layer implementation for incoming and outgoing IP traffic.
+ *
+ * @see ip_frag.c
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip_frag.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/igmp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/stats.h"
+#include "arch/perf.h"
+
+#include <string.h>
+
+/** Set this to 0 in the rare case of wanting to call an extra function to
+ * generate the IP checksum (in contrast to calculating it on-the-fly). */
+#ifndef LWIP_INLINE_IP_CHKSUM
+#define LWIP_INLINE_IP_CHKSUM 1
+#endif
+#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP
+#define CHECKSUM_GEN_IP_INLINE 1
+#else
+#define CHECKSUM_GEN_IP_INLINE 0
+#endif
+
+#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT)
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1
+
+/** Some defines for DHCP to let link-layer-addressed packets through while the
+ * netif is down.
+ * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT
+ * to return 1 if the port is accepted and 0 if the port is not accepted.
+ */
+#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT)
+/* accept DHCP client port and custom port */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \
+ || (LWIP_IP_ACCEPT_UDP_PORT(port)))
+#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept custom port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(dst_port))
+#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept DHCP client port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT))
+#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+
+#else /* LWIP_DHCP */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
+#endif /* LWIP_DHCP */
+
+/**
+ * The interface that provided the packet for the current callback
+ * invocation.
+ */
+struct netif *current_netif;
+
+/**
+ * Header of the input packet currently being processed.
+ */
+const struct ip_hdr *current_header;
+/** Source IP address of current_header */
+ip_addr_t current_iphdr_src;
+/** Destination IP address of current_header */
+ip_addr_t current_iphdr_dest;
+
+/** The IP header ID of the next outgoing IP packet */
+static u16_t ip_id;
+
+/**
+ * Finds the appropriate network interface for a given IP address. It
+ * searches the list of network interfaces linearly. A match is found
+ * if the masked IP address of the network interface equals the masked
+ * IP address given to the function.
+ *
+ * @param dest the destination IP address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip_route(ip_addr_t *dest)
+{
+ struct netif *netif;
+
+ /* iterate through netifs */
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ /* network mask matches? */
+ if (netif_is_up(netif)) {
+ if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
+ /* return netif on which to forward IP packet */
+ return netif;
+ }
+ }
+ }
+ if ((netif_default == NULL) || (!netif_is_up(netif_default))) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ snmp_inc_ipoutnoroutes();
+ return NULL;
+ }
+ /* no matching netif found, use default netif */
+ return netif_default;
+}
+
+#if IP_FORWARD
+/**
+ * Forwards an IP packet. It finds an appropriate route for the
+ * packet, decrements the TTL value of the packet, adjusts the
+ * checksum and outputs the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IP header of the input packet
+ * @param inp the netif on which this packet was received
+ */
+static void
+ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
+{
+ struct netif *netif;
+
+ PERF_START;
+
+ /* RFC3927 2.7: do not forward link-local addresses */
+ if (ip_addr_islinklocal(¤t_iphdr_dest)) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest),
+ ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest)));
+ goto return_noroute;
+ }
+
+ /* Find network interface where to forward this IP packet to. */
+ netif = ip_route(¤t_iphdr_dest);
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
+ ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest),
+ ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest)));
+ goto return_noroute;
+ }
+ /* Do not forward packets onto the same network interface on which
+ * they arrived. */
+ if (netif == inp) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n"));
+ goto return_noroute;
+ }
+
+ /* decrement TTL */
+ IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
+ /* send ICMP if TTL == 0 */
+ if (IPH_TTL(iphdr) == 0) {
+ snmp_inc_ipinhdrerrors();
+#if LWIP_ICMP
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
+ icmp_time_exceeded(p, ICMP_TE_TTL);
+ }
+#endif /* LWIP_ICMP */
+ return;
+ }
+
+ /* Incrementally update the IP checksum. */
+ if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffff - 0x100)) {
+ IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1);
+ } else {
+ IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100));
+ }
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest),
+ ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest)));
+
+ IP_STATS_INC(ip.fw);
+ IP_STATS_INC(ip.xmit);
+ snmp_inc_ipforwdatagrams();
+
+ PERF_STOP("ip_forward");
+ /* transmit pbuf on chosen interface */
+ netif->output(netif, p, ¤t_iphdr_dest);
+ return;
+return_noroute:
+ snmp_inc_ipoutnoroutes();
+}
+#endif /* IP_FORWARD */
+
+/**
+ * This function is called by the network interface device driver when
+ * an IP packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip_forward). The IP checksum is always checked.
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IP packet (p->payload points to IP header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ * processed, but currently always returns ERR_OK)
+ */
+err_t
+ip_input(struct pbuf *p, struct netif *inp)
+{
+ struct ip_hdr *iphdr;
+ struct netif *netif;
+ u16_t iphdr_hlen;
+ u16_t iphdr_len;
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ int check_ip_src=1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ IP_STATS_INC(ip.recv);
+ snmp_inc_ipinreceives();
+
+ /* identify the IP header */
+ iphdr = (struct ip_hdr *)p->payload;
+ if (IPH_V(iphdr) != 4) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr)));
+ ip_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.err);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinhdrerrors();
+ return ERR_OK;
+ }
+
+ /* obtain IP header length in number of 32-bit words */
+ iphdr_hlen = IPH_HL(iphdr);
+ /* calculate IP header length in bytes */
+ iphdr_hlen *= 4;
+ /* obtain ip length in bytes */
+ iphdr_len = ntohs(IPH_LEN(iphdr));
+
+ /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+ if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
+ if (iphdr_hlen > p->len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_hlen, p->len));
+ }
+ if (iphdr_len > p->tot_len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_len, p->tot_len));
+ }
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.lenerr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipindiscards();
+ return ERR_OK;
+ }
+
+ /* verify checksum */
+#if CHECKSUM_CHECK_IP
+ if (inet_chksum(iphdr, iphdr_hlen) != 0) {
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
+ ip_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.chkerr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinhdrerrors();
+ return ERR_OK;
+ }
+#endif
+
+ /* Trim pbuf. This should have been done at the netif layer,
+ * but we'll do it anyway just to be sure that its done. */
+ pbuf_realloc(p, iphdr_len);
+
+ /* copy IP addresses to aligned ip_addr_t */
+ ip_addr_copy(current_iphdr_dest, iphdr->dest);
+ ip_addr_copy(current_iphdr_src, iphdr->src);
+
+ /* match packet against an interface, i.e. is this packet for us? */
+#if LWIP_IGMP
+ if (ip_addr_ismulticast(¤t_iphdr_dest)) {
+ if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ¤t_iphdr_dest))) {
+ netif = inp;
+ } else {
+ netif = NULL;
+ }
+ } else
+#endif /* LWIP_IGMP */
+ {
+ /* start trying with inp. if that's not acceptable, start walking the
+ list of configured netifs.
+ 'first' is used as a boolean to mark whether we started walking the list */
+ int first = 1;
+ netif = inp;
+ do {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
+ ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(&netif->ip_addr),
+ ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask),
+ ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask),
+ ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask)));
+
+ /* interface is up and configured? */
+ if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {
+ /* unicast to this interface address? */
+ if (ip_addr_cmp(¤t_iphdr_dest, &(netif->ip_addr)) ||
+ /* or broadcast on this interface network address? */
+ ip_addr_isbroadcast(¤t_iphdr_dest, netif)) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* break out of for loop */
+ break;
+ }
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist after changing
+ the netif's address (RFC3927 ch. 1.9) */
+ if ((netif->autoip != NULL) &&
+ ip_addr_cmp(¤t_iphdr_dest, &(netif->autoip->llipaddr))) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* break out of for loop */
+ break;
+ }
+#endif /* LWIP_AUTOIP */
+ }
+ if (first) {
+ first = 0;
+ netif = netif_list;
+ } else {
+ netif = netif->next;
+ }
+ if (netif == inp) {
+ netif = netif->next;
+ }
+ } while(netif != NULL);
+ }
+
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
+ * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
+ * According to RFC 1542 section 3.1.1, referred by RFC 2131).
+ *
+ * If you want to accept private broadcast communication while a netif is down,
+ * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.:
+ *
+ * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345))
+ */
+ if (netif == NULL) {
+ /* remote port is DHCP server? */
+ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
+ struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n",
+ ntohs(udphdr->dest)));
+ if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n"));
+ netif = inp;
+ check_ip_src = 0;
+ }
+ }
+ }
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */
+ if (check_ip_src && !ip_addr_isany(¤t_iphdr_src))
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+ { if ((ip_addr_isbroadcast(¤t_iphdr_src, inp)) ||
+ (ip_addr_ismulticast(¤t_iphdr_src))) {
+ /* packet source is not valid */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n"));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinaddrerrors();
+ snmp_inc_ipindiscards();
+ return ERR_OK;
+ }
+ }
+
+ /* packet not for us? */
+ if (netif == NULL) {
+ /* packet not for us, route or discard */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n"));
+#if IP_FORWARD
+ /* non-broadcast packet? */
+ if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp)) {
+ /* try to forward IP packet on (other) interfaces */
+ ip_forward(p, iphdr, inp);
+ } else
+#endif /* IP_FORWARD */
+ {
+ snmp_inc_ipinaddrerrors();
+ snmp_inc_ipindiscards();
+ }
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ /* packet consists of multiple fragments? */
+ if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
+#if IP_REASSEMBLY /* packet fragment reassembly code present? */
+ LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n",
+ ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8));
+ /* reassemble the packet*/
+ p = ip_reass(p);
+ /* packet not fully reassembled yet? */
+ if (p == NULL) {
+ return ERR_OK;
+ }
+ iphdr = (struct ip_hdr *)p->payload;
+#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
+ pbuf_free(p);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
+ ntohs(IPH_OFFSET(iphdr))));
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ snmp_inc_ipinunknownprotos();
+ return ERR_OK;
+#endif /* IP_REASSEMBLY */
+ }
+
+#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */
+
+#if LWIP_IGMP
+ /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */
+ if((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
+#else
+ if (iphdr_hlen > IP_HLEN) {
+#endif /* LWIP_IGMP */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n"));
+ pbuf_free(p);
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ snmp_inc_ipinunknownprotos();
+ return ERR_OK;
+ }
+#endif /* IP_OPTIONS_ALLOWED == 0 */
+
+ /* send to upper layers */
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n"));
+ ip_debug_print(p);
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+ current_netif = inp;
+ current_header = iphdr;
+
+#if LWIP_RAW
+ /* raw input did not eat the packet? */
+ if (raw_input(p, inp) == 0)
+#endif /* LWIP_RAW */
+ {
+
+ switch (IPH_PROTO(iphdr)) {
+#if LWIP_UDP
+ case IP_PROTO_UDP:
+#if LWIP_UDPLITE
+ case IP_PROTO_UDPLITE:
+#endif /* LWIP_UDPLITE */
+ snmp_inc_ipindelivers();
+ udp_input(p, inp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case IP_PROTO_TCP:
+ snmp_inc_ipindelivers();
+ tcp_input(p, inp);
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP
+ case IP_PROTO_ICMP:
+ snmp_inc_ipindelivers();
+ icmp_input(p, inp);
+ break;
+#endif /* LWIP_ICMP */
+#if LWIP_IGMP
+ case IP_PROTO_IGMP:
+ igmp_input(p, inp, ¤t_iphdr_dest);
+ break;
+#endif /* LWIP_IGMP */
+ default:
+#if LWIP_ICMP
+ /* send ICMP destination protocol unreachable unless is was a broadcast */
+ if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp) &&
+ !ip_addr_ismulticast(¤t_iphdr_dest)) {
+ p->payload = iphdr;
+ icmp_dest_unreach(p, ICMP_DUR_PROTO);
+ }
+#endif /* LWIP_ICMP */
+ pbuf_free(p);
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr)));
+
+ IP_STATS_INC(ip.proterr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinunknownprotos();
+ }
+ }
+
+ current_netif = NULL;
+ current_header = NULL;
+ ip_addr_set_any(¤t_iphdr_src);
+ ip_addr_set_any(¤t_iphdr_dest);
+
+ return ERR_OK;
+}
+
+/**
+ * Sends an IP packet on a network interface. This function constructs
+ * the IP header and calculates the IP header checksum. If the source
+ * IP address is NULL, the IP address of the outgoing network
+ * interface is filled in as source address.
+ * If the destination IP address is IP_HDRINCL, p is assumed to already
+ * include an IP header and p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ *
+ * @note ip_id: RFC791 "some host may be able to simply use
+ * unique identifiers independent of destination"
+ */
+err_t
+ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos,
+ u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+ return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if() but with the possibility to include IP options:
+ *
+ * @ param ip_options pointer to the IP options, copied into the IP header
+ * @ param optlen length of ip_options
+ */
+err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+ struct ip_hdr *iphdr;
+ ip_addr_t dest_addr;
+#if CHECKSUM_GEN_IP_INLINE
+ u32_t chk_sum = 0;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ snmp_inc_ipoutrequests();
+
+ /* Should the IP header be generated or is it already included in p? */
+ if (dest != IP_HDRINCL) {
+ u16_t ip_hlen = IP_HLEN;
+#if IP_OPTIONS_SEND
+ u16_t optlen_aligned = 0;
+ if (optlen != 0) {
+#if CHECKSUM_GEN_IP_INLINE
+ int i;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ /* round up to a multiple of 4 */
+ optlen_aligned = ((optlen + 3) & ~3);
+ ip_hlen += optlen_aligned;
+ /* First write in the IP options */
+ if (pbuf_header(p, optlen_aligned)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n"));
+ IP_STATS_INC(ip.err);
+ snmp_inc_ipoutdiscards();
+ return ERR_BUF;
+ }
+ MEMCPY(p->payload, ip_options, optlen);
+ if (optlen < optlen_aligned) {
+ /* zero the remaining bytes */
+ memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen);
+ }
+#if CHECKSUM_GEN_IP_INLINE
+ for (i = 0; i < optlen_aligned; i += sizeof(u16_t)) {
+ chk_sum += ((u16_t*)p->payload)[i];
+ }
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ }
+#endif /* IP_OPTIONS_SEND */
+ /* generate IP header */
+ if (pbuf_header(p, IP_HLEN)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n"));
+
+ IP_STATS_INC(ip.err);
+ snmp_inc_ipoutdiscards();
+ return ERR_BUF;
+ }
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
+ (p->len >= sizeof(struct ip_hdr)));
+
+ IPH_TTL_SET(iphdr, ttl);
+ IPH_PROTO_SET(iphdr, proto);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += LWIP_MAKE_U16(proto, ttl);
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ /* dest cannot be NULL here */
+ ip_addr_copy(iphdr->dest, *dest);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ IPH_VHLTOS_SET(iphdr, 4, ip_hlen / 4, tos);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_v_hl_tos;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_LEN_SET(iphdr, htons(p->tot_len));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_len;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_OFFSET_SET(iphdr, 0);
+ IPH_ID_SET(iphdr, htons(ip_id));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_id;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ ++ip_id;
+
+ if (ip_addr_isany(src)) {
+ ip_addr_copy(iphdr->src, netif->ip_addr);
+ } else {
+ /* src cannot be NULL here */
+ ip_addr_copy(iphdr->src, *src);
+ }
+
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;
+ chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
+ chk_sum = (chk_sum >> 16) + chk_sum;
+ chk_sum = ~chk_sum;
+ iphdr->_chksum = chk_sum; /* network order */
+#else /* CHECKSUM_GEN_IP_INLINE */
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
+#endif
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ } else {
+ /* IP header already included in p */
+ iphdr = (struct ip_hdr *)p->payload;
+ ip_addr_copy(dest_addr, iphdr->dest);
+ dest = &dest_addr;
+ }
+
+ IP_STATS_INC(ip.xmit);
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
+ ip_debug_print(p);
+
+#if ENABLE_LOOPBACK
+ if (ip_addr_cmp(dest, &netif->ip_addr)) {
+ /* Packet to self, enqueue it for loopback */
+ LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()"));
+ return netif_loop_output(netif, p, dest);
+ }
+#if LWIP_IGMP
+ if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
+ netif_loop_output(netif, p, dest);
+ }
+#endif /* LWIP_IGMP */
+#endif /* ENABLE_LOOPBACK */
+#if IP_FRAG
+ /* don't fragment if interface has mtu set to 0 [loopif] */
+ if (netif->mtu && (p->tot_len > netif->mtu)) {
+ return ip_frag(p, netif, dest);
+ }
+#endif /* IP_FRAG */
+
+ LWIP_DEBUGF(IP_DEBUG, ("netif->output()"));
+ return netif->output(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip_output_if. It finds the outgoing network
+ * interface and calls upon ip_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto)
+{
+ struct netif *netif;
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ if ((netif = ip_route(dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ return ip_output_if(p, src, dest, ttl, tos, proto, netif);
+}
+
+#if LWIP_NETIF_HWADDRHINT
+/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ * before calling ip_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param addr_hint address hint pointer set to netif->addr_hint before
+ * calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
+{
+ struct netif *netif;
+ err_t err;
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ if ((netif = ip_route(dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ netif->addr_hint = addr_hint;
+ err = ip_output_if(p, src, dest, ttl, tos, proto, netif);
+ netif->addr_hint = NULL;
+
+ return err;
+}
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+#if IP_DEBUG
+/* Print an IP header by using LWIP_DEBUGF
+ * @param p an IP packet, p->payload pointing to the IP header
+ */
+void
+ip_debug_print(struct pbuf *p)
+{
+ struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+ u8_t *payload;
+
+ payload = (u8_t *)iphdr + IP_HLEN;
+
+ LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n",
+ IPH_V(iphdr),
+ IPH_HL(iphdr),
+ IPH_TOS(iphdr),
+ ntohs(IPH_LEN(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n",
+ ntohs(IPH_ID(iphdr)),
+ ntohs(IPH_OFFSET(iphdr)) >> 15 & 1,
+ ntohs(IPH_OFFSET(iphdr)) >> 14 & 1,
+ ntohs(IPH_OFFSET(iphdr)) >> 13 & 1,
+ ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n",
+ IPH_TTL(iphdr),
+ IPH_PROTO(iphdr),
+ ntohs(IPH_CHKSUM(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n",
+ ip4_addr1_16(&iphdr->src),
+ ip4_addr2_16(&iphdr->src),
+ ip4_addr3_16(&iphdr->src),
+ ip4_addr4_16(&iphdr->src)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n",
+ ip4_addr1_16(&iphdr->dest),
+ ip4_addr2_16(&iphdr->dest),
+ ip4_addr3_16(&iphdr->dest),
+ ip4_addr4_16(&iphdr->dest)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/ipv4/ip_addr.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,312 @@
+/**
+ * @file
+ * This is the IPv4 address tools implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
+const ip_addr_t ip_addr_any = { IPADDR_ANY };
+const ip_addr_t ip_addr_broadcast = { IPADDR_BROADCAST };
+
+/**
+ * Determine if an address is a broadcast address on a network interface
+ *
+ * @param addr address to be checked
+ * @param netif the network interface against which the address is checked
+ * @return returns non-zero if the address is a broadcast address
+ */
+u8_t
+ip4_addr_isbroadcast(u32_t addr, const struct netif *netif)
+{
+ ip_addr_t ipaddr;
+ ip4_addr_set_u32(&ipaddr, addr);
+
+ /* all ones (broadcast) or all zeroes (old skool broadcast) */
+ if ((~addr == IPADDR_ANY) ||
+ (addr == IPADDR_ANY)) {
+ return 1;
+ /* no broadcast support on this network interface? */
+ } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
+ /* the given address cannot be a broadcast address
+ * nor can we check against any broadcast addresses */
+ return 0;
+ /* address matches network interface address exactly? => no broadcast */
+ } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) {
+ return 0;
+ /* on the same (sub) network... */
+ } else if (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask))
+ /* ...and host identifier bits are all ones? =>... */
+ && ((addr & ~ip4_addr_get_u32(&netif->netmask)) ==
+ (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) {
+ /* => network broadcast address */
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/** Checks if a netmask is valid (starting with ones, then only zeros)
+ *
+ * @param netmask the IPv4 netmask to check (in network byte order!)
+ * @return 1 if the netmask is valid, 0 if it is not
+ */
+u8_t
+ip4_addr_netmask_valid(u32_t netmask)
+{
+ u32_t mask;
+ u32_t nm_hostorder = lwip_htonl(netmask);
+
+ /* first, check for the first zero */
+ for (mask = 1U << 31 ; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) == 0) {
+ break;
+ }
+ }
+ /* then check that there is no one */
+ for (; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) != 0) {
+ /* there is a one after the first zero -> invalid */
+ return 0;
+ }
+ }
+ /* no one after the first zero -> valid */
+ return 1;
+}
+
+/* Here for now until needed in other places in lwIP */
+#ifndef isprint
+#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
+#define isprint(c) in_range(c, 0x20, 0x7f)
+#define isdigit(c) in_range(c, '0', '9')
+#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
+#define islower(c) in_range(c, 'a', 'z')
+#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
+#endif
+
+/**
+ * Ascii internet address interpretation routine.
+ * The value returned is in network order.
+ *
+ * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
+ * @return ip address in network order
+ */
+u32_t
+ipaddr_addr(const char *cp)
+{
+ ip_addr_t val;
+
+ if (ipaddr_aton(cp, &val)) {
+ return ip4_addr_get_u32(&val);
+ }
+ return (IPADDR_NONE);
+}
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ *
+ * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ipaddr_aton(const char *cp, ip_addr_t *addr)
+{
+ u32_t val;
+ u8_t base;
+ char c;
+ u32_t parts[4];
+ u32_t *pp = parts;
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, 1-9=decimal.
+ */
+ if (!isdigit(c))
+ return (0);
+ val = 0;
+ base = 10;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ c = *++cp;
+ } else
+ base = 8;
+ }
+ for (;;) {
+ if (isdigit(c)) {
+ val = (val * base) + (int)(c - '0');
+ c = *++cp;
+ } else if (base == 16 && isxdigit(c)) {
+ val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A'));
+ c = *++cp;
+ } else
+ break;
+ }
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3) {
+ return (0);
+ }
+ *pp++ = val;
+ c = *++cp;
+ } else
+ break;
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && !isspace(c)) {
+ return (0);
+ }
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ switch (pp - parts + 1) {
+
+ case 0:
+ return (0); /* initial nondigit */
+
+ case 1: /* a -- 32 bits */
+ break;
+
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffffUL) {
+ return (0);
+ }
+ val |= parts[0] << 24;
+ break;
+
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff) {
+ return (0);
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff) {
+ return (0);
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ default:
+ LWIP_ASSERT("unhandled", 0);
+ break;
+ }
+ if (addr) {
+ ip4_addr_set_u32(addr, htonl(val));
+ }
+ return (1);
+}
+
+/**
+ * Convert numeric IP address into decimal dotted ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ * represenation of addr
+ */
+char *
+ipaddr_ntoa(const ip_addr_t *addr)
+{
+ static char str[16];
+ return ipaddr_ntoa_r(addr, str, 16);
+}
+
+/**
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
+{
+ u32_t s_addr;
+ char inv[3];
+ char *rp;
+ u8_t *ap;
+ u8_t rem;
+ u8_t n;
+ u8_t i;
+ int len = 0;
+
+ s_addr = ip4_addr_get_u32(addr);
+
+ rp = buf;
+ ap = (u8_t *)&s_addr;
+ for(n = 0; n < 4; n++) {
+ i = 0;
+ do {
+ rem = *ap % (u8_t)10;
+ *ap /= (u8_t)10;
+ inv[i++] = '0' + rem;
+ } while(*ap);
+ while(i--) {
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = inv[i];
+ }
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = '.';
+ ap++;
+ }
+ *--rp = 0;
+ return buf;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/ipv4/ip_frag.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,863 @@
+/**
+ * @file
+ * This is the IPv4 packet segmentation and reassembly implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ * Simon Goldschmidt
+ * original reassembly code by Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip_frag.h"
+#include "lwip/def.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/snmp.h"
+#include "lwip/stats.h"
+#include "lwip/icmp.h"
+
+#include <string.h>
+
+#if IP_REASSEMBLY
+/**
+ * The IP reassembly code currently has the following limitations:
+ * - IP header options are not supported
+ * - fragments must not overlap (e.g. due to different routes),
+ * currently, overlapping or duplicate fragments are thrown away
+ * if IP_REASS_CHECK_OVERLAP=1 (the default)!
+ *
+ * @todo: work with IP header options
+ */
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IP header, since it replaces
+ * the IP header in memory in incoming fragments (after copying it) to keep
+ * track of the various fragments. (-> If the IP header doesn't need packing,
+ * this struct doesn't need packing, too.)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_reass_helper {
+ PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+ PACK_STRUCT_FIELD(u16_t start);
+ PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
+ (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+ ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+ IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
+
+/* global variables */
+static struct ip_reassdata *reassdatagrams;
+static u16_t ip_reass_pbufcount;
+
+/* function prototypes */
+static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+
+/**
+ * Reassembly timer base function
+ * for both NO_SYS == 0 and 1 (!).
+ *
+ * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
+ */
+void
+ip_reass_tmr(void)
+{
+ struct ip_reassdata *r, *prev = NULL;
+
+ r = reassdatagrams;
+ while (r != NULL) {
+ /* Decrement the timer. Once it reaches 0,
+ * clean up the incomplete fragment assembly */
+ if (r->timer > 0) {
+ r->timer--;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
+ prev = r;
+ r = r->next;
+ } else {
+ /* reassembly timed out */
+ struct ip_reassdata *tmp;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
+ tmp = r;
+ /* get the next pointer before freeing */
+ r = r->next;
+ /* free the helper struct and all enqueued pbufs */
+ ip_reass_free_complete_datagram(tmp, prev);
+ }
+ }
+}
+
+/**
+ * Free a datagram (struct ip_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
+ * SNMP counters and sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ * @param prev the previous datagram in the linked list
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+ u16_t pbufs_freed = 0;
+ u8_t clen;
+ struct pbuf *p;
+ struct ip_reass_helper *iprh;
+
+ LWIP_ASSERT("prev != ipr", prev != ipr);
+ if (prev != NULL) {
+ LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
+ }
+
+ snmp_inc_ipreasmfails();
+#if LWIP_ICMP
+ iprh = (struct ip_reass_helper *)ipr->p->payload;
+ if (iprh->start == 0) {
+ /* The first fragment was received, send ICMP time exceeded. */
+ /* First, de-queue the first pbuf from r->p. */
+ p = ipr->p;
+ ipr->p = iprh->next_pbuf;
+ /* Then, copy the original header into it. */
+ SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
+ icmp_time_exceeded(p, ICMP_TE_FRAG);
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(p);
+ }
+#endif /* LWIP_ICMP */
+
+ /* First, free all received pbufs. The individual pbufs need to be released
+ separately as they have not yet been chained */
+ p = ipr->p;
+ while (p != NULL) {
+ struct pbuf *pcur;
+ iprh = (struct ip_reass_helper *)p->payload;
+ pcur = p;
+ /* get the next pointer before freeing */
+ p = iprh->next_pbuf;
+ clen = pbuf_clen(pcur);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(pcur);
+ }
+ /* Then, unchain the struct ip_reassdata from the list and free it. */
+ ip_reass_dequeue_datagram(ipr, prev);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
+ ip_reass_pbufcount -= pbufs_freed;
+
+ return pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram 'fraghdr' belongs to is not freed!
+ *
+ * @param fraghdr IP header of the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ * (used for freeing other datagrams if not enough space)
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
+{
+ /* @todo Can't we simply remove the last datagram in the
+ * linked list behind reassdatagrams?
+ */
+ struct ip_reassdata *r, *oldest, *prev;
+ int pbufs_freed = 0, pbufs_freed_current;
+ int other_datagrams;
+
+ /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+ * but don't free the datagram that 'fraghdr' belongs to! */
+ do {
+ oldest = NULL;
+ prev = NULL;
+ other_datagrams = 0;
+ r = reassdatagrams;
+ while (r != NULL) {
+ if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
+ /* Not the same datagram as fraghdr */
+ other_datagrams++;
+ if (oldest == NULL) {
+ oldest = r;
+ } else if (r->timer <= oldest->timer) {
+ /* older than the previous oldest */
+ oldest = r;
+ }
+ }
+ if (r->next != NULL) {
+ prev = r;
+ }
+ r = r->next;
+ }
+ if (oldest != NULL) {
+ pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
+ pbufs_freed += pbufs_freed_current;
+ }
+ } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
+ return pbufs_freed;
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Enqueues a new fragment into the fragment queue
+ * @param fraghdr points to the new fragments IP hdr
+ * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
+ * @return A pointer to the queue location into which the fragment was enqueued
+ */
+static struct ip_reassdata*
+ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
+{
+ struct ip_reassdata* ipr;
+ /* No matching previous fragment found, allocate a new reassdata struct */
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+ if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ }
+ if (ipr == NULL)
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
+ return NULL;
+ }
+ }
+ memset(ipr, 0, sizeof(struct ip_reassdata));
+ ipr->timer = IP_REASS_MAXAGE;
+
+ /* enqueue the new structure to the front of the list */
+ ipr->next = reassdatagrams;
+ reassdatagrams = ipr;
+ /* copy the ip header for later tests and input */
+ /* @todo: no ip options supported? */
+ SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
+ return ipr;
+}
+
+/**
+ * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
+ * @param ipr points to the queue entry to dequeue
+ */
+static void
+ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+
+ /* dequeue the reass struct */
+ if (reassdatagrams == ipr) {
+ /* it was the first in the list */
+ reassdatagrams = ipr->next;
+ } else {
+ /* it wasn't the first, so it must have a valid 'prev' */
+ LWIP_ASSERT("sanity check linked list", prev != NULL);
+ prev->next = ipr->next;
+ }
+
+ /* now we can free the ip_reass struct */
+ memp_free(MEMP_REASSDATA, ipr);
+}
+
+/**
+ * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list
+ * will grow over time as new pbufs are rx.
+ * Also checks that the datagram passes basic continuity checks (if the last
+ * fragment was received at least once).
+ * @param root_p points to the 'root' pbuf for the current datagram being assembled.
+ * @param new_p points to the pbuf for the current fragment
+ * @return 0 if invalid, >0 otherwise
+ */
+static int
+ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
+{
+ struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+ struct pbuf *q;
+ u16_t offset,len;
+ struct ip_hdr *fraghdr;
+ int valid = 1;
+
+ /* Extract length and fragment offset from current fragment */
+ fraghdr = (struct ip_hdr*)new_p->payload;
+ len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+ offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+
+ /* overwrite the fragment's ip header from the pbuf with our helper struct,
+ * and setup the embedded helper structure. */
+ /* make sure the struct ip_reass_helper fits into the IP header */
+ LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
+ sizeof(struct ip_reass_helper) <= IP_HLEN);
+ iprh = (struct ip_reass_helper*)new_p->payload;
+ iprh->next_pbuf = NULL;
+ iprh->start = offset;
+ iprh->end = offset + len;
+
+ /* Iterate through until we either get to the end of the list (append),
+ * or we find on with a larger offset (insert). */
+ for (q = ipr->p; q != NULL;) {
+ iprh_tmp = (struct ip_reass_helper*)q->payload;
+ if (iprh->start < iprh_tmp->start) {
+ /* the new pbuf should be inserted before this */
+ iprh->next_pbuf = q;
+ if (iprh_prev != NULL) {
+ /* not the fragment with the lowest offset */
+#if IP_REASS_CHECK_OVERLAP
+ if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
+ /* fragment overlaps with previous or following, throw away */
+ goto freepbuf;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ } else {
+ /* fragment with the lowest offset */
+ ipr->p = new_p;
+ }
+ break;
+ } else if(iprh->start == iprh_tmp->start) {
+ /* received the same datagram twice: no need to keep the datagram */
+ goto freepbuf;
+#if IP_REASS_CHECK_OVERLAP
+ } else if(iprh->start < iprh_tmp->end) {
+ /* overlap: no need to keep the new datagram */
+ goto freepbuf;
+#endif /* IP_REASS_CHECK_OVERLAP */
+ } else {
+ /* Check if the fragments received so far have no wholes. */
+ if (iprh_prev != NULL) {
+ if (iprh_prev->end != iprh_tmp->start) {
+ /* There is a fragment missing between the current
+ * and the previous fragment */
+ valid = 0;
+ }
+ }
+ }
+ q = iprh_tmp->next_pbuf;
+ iprh_prev = iprh_tmp;
+ }
+
+ /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+ if (q == NULL) {
+ if (iprh_prev != NULL) {
+ /* this is (for now), the fragment with the highest offset:
+ * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ }
+ } else {
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+ ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ /* this is the first fragment we ever received for this ip datagram */
+ ipr->p = new_p;
+ }
+ }
+
+ /* At this point, the validation part begins: */
+ /* If we already received the last fragment */
+ if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
+ /* and had no wholes so far */
+ if (valid) {
+ /* then check if the rest of the fragments is here */
+ /* Check if the queue starts with the first datagram */
+ if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
+ valid = 0;
+ } else {
+ /* and check that there are no wholes after this datagram */
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ while (q != NULL) {
+ iprh = (struct ip_reass_helper*)q->payload;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ break;
+ }
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ }
+ /* if still valid, all fragments are received
+ * (because to the MF==0 already arrived */
+ if (valid) {
+ LWIP_ASSERT("sanity check", ipr->p != NULL);
+ LWIP_ASSERT("sanity check",
+ ((struct ip_reass_helper*)ipr->p->payload) != iprh);
+ LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
+ iprh->next_pbuf == NULL);
+ LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
+ iprh->end == ipr->datagram_len);
+ }
+ }
+ }
+ /* If valid is 0 here, there are some fragments missing in the middle
+ * (since MF == 0 has already arrived). Such datagrams simply time out if
+ * no more fragments are received... */
+ return valid;
+ }
+ /* If we come here, not all fragments were received, yet! */
+ return 0; /* not yet valid! */
+#if IP_REASS_CHECK_OVERLAP
+freepbuf:
+ ip_reass_pbufcount -= pbuf_clen(new_p);
+ pbuf_free(new_p);
+ return 0;
+#endif /* IP_REASS_CHECK_OVERLAP */
+}
+
+/**
+ * Reassembles incoming IP fragments into an IP datagram.
+ *
+ * @param p points to a pbuf chain of the fragment
+ * @return NULL if reassembly is incomplete, ? otherwise
+ */
+struct pbuf *
+ip_reass(struct pbuf *p)
+{
+ struct pbuf *r;
+ struct ip_hdr *fraghdr;
+ struct ip_reassdata *ipr;
+ struct ip_reass_helper *iprh;
+ u16_t offset, len;
+ u8_t clen;
+ struct ip_reassdata *ipr_prev = NULL;
+
+ IPFRAG_STATS_INC(ip_frag.recv);
+ snmp_inc_ipreasmreqds();
+
+ fraghdr = (struct ip_hdr*)p->payload;
+
+ if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
+ IPFRAG_STATS_INC(ip_frag.err);
+ goto nullreturn;
+ }
+
+ offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+ len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+
+ /* Check if we are allowed to enqueue more datagrams. */
+ clen = pbuf_clen(p);
+ if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+ if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
+ ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ /* No datagram could be freed and still too many pbufs enqueued */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
+ ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ /* @todo: send ICMP time exceeded here? */
+ /* drop this pbuf */
+ goto nullreturn;
+ }
+ }
+
+ /* Look for the datagram the fragment belongs to in the current datagram queue,
+ * remembering the previous in the queue for later dequeueing. */
+ for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
+ /* Check if the incoming fragment matches the one currently present
+ in the reassembly buffer. If so, we proceed with copying the
+ fragment into the buffer. */
+ if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
+ ntohs(IPH_ID(fraghdr))));
+ IPFRAG_STATS_INC(ip_frag.cachehit);
+ break;
+ }
+ ipr_prev = ipr;
+ }
+
+ if (ipr == NULL) {
+ /* Enqueue a new datagram into the datagram queue */
+ ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
+ /* Bail if unable to enqueue */
+ if(ipr == NULL) {
+ goto nullreturn;
+ }
+ } else {
+ if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
+ ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
+ /* ipr->iphdr is not the header from the first fragment, but fraghdr is
+ * -> copy fraghdr into ipr->iphdr since we want to have the header
+ * of the first fragment (for ICMP time exceeded and later, for copying
+ * all options, if supported)*/
+ SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
+ }
+ }
+ /* Track the current number of pbufs current 'in-flight', in order to limit
+ the number of fragments that may be enqueued at any one time */
+ ip_reass_pbufcount += clen;
+
+ /* At this point, we have either created a new entry or pointing
+ * to an existing one */
+
+ /* check for 'no more fragments', and update queue entry*/
+ if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
+ ipr->flags |= IP_REASS_FLAG_LASTFRAG;
+ ipr->datagram_len = offset + len;
+ LWIP_DEBUGF(IP_REASS_DEBUG,
+ ("ip_reass: last fragment seen, total len %"S16_F"\n",
+ ipr->datagram_len));
+ }
+ /* find the right place to insert this pbuf */
+ /* @todo: trim pbufs if fragments are overlapping */
+ if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
+ /* the totally last fragment (flag more fragments = 0) was received at least
+ * once AND all fragments are received */
+ ipr->datagram_len += IP_HLEN;
+
+ /* save the second pbuf before copying the header over the pointer */
+ r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
+
+ /* copy the original ip header back to the first pbuf */
+ fraghdr = (struct ip_hdr*)(ipr->p->payload);
+ SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
+ IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
+ IPH_OFFSET_SET(fraghdr, 0);
+ IPH_CHKSUM_SET(fraghdr, 0);
+ /* @todo: do we need to set calculate the correct checksum? */
+ IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+
+ p = ipr->p;
+
+ /* chain together the pbufs contained within the reass_data list. */
+ while(r != NULL) {
+ iprh = (struct ip_reass_helper*)r->payload;
+
+ /* hide the ip header for every succeding fragment */
+ pbuf_header(r, -IP_HLEN);
+ pbuf_cat(p, r);
+ r = iprh->next_pbuf;
+ }
+ /* release the sources allocate for the fragment queue entry */
+ ip_reass_dequeue_datagram(ipr, ipr_prev);
+
+ /* and adjust the number of pbufs currently queued for reassembly. */
+ ip_reass_pbufcount -= pbuf_clen(p);
+
+ /* Return the pbuf chain */
+ return p;
+ }
+ /* the datagram is not (yet?) reassembled completely */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
+ return NULL;
+
+nullreturn:
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
+ IPFRAG_STATS_INC(ip_frag.drop);
+ pbuf_free(p);
+ return NULL;
+}
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if IP_FRAG_USES_STATIC_BUF
+static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
+#else /* IP_FRAG_USES_STATIC_BUF */
+
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref*
+ip_frag_alloc_pbuf_custom_ref(void)
+{
+ return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+ LWIP_ASSERT("p != NULL", p != NULL);
+ memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ipfrag_free_pbuf_custom(struct pbuf *p)
+{
+ struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+ LWIP_ASSERT("pcr != NULL", pcr != NULL);
+ LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+ if (pcr->original != NULL) {
+ pbuf_free(pcr->original);
+ }
+ ip_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+/**
+ * Fragment an IP datagram if too large for the netif.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by using a fixed size static memory buffer (PBUF_REF) or
+ * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
+ *
+ * @param p ip packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ip address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
+{
+ struct pbuf *rambuf;
+#if IP_FRAG_USES_STATIC_BUF
+ struct pbuf *header;
+#else
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+ struct pbuf *newpbuf;
+#endif
+ struct ip_hdr *original_iphdr;
+#endif
+ struct ip_hdr *iphdr;
+ u16_t nfb;
+ u16_t left, cop;
+ u16_t mtu = netif->mtu;
+ u16_t ofo, omf;
+ u16_t last;
+ u16_t poff = IP_HLEN;
+ u16_t tmp;
+#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
+#endif
+
+ /* Get a RAM based MTU sized pbuf */
+#if IP_FRAG_USES_STATIC_BUF
+ /* When using a static buffer, we use a PBUF_REF, which we will
+ * use to reference the packet (without link header).
+ * Layer and length is irrelevant.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
+ if (rambuf == NULL) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
+ return ERR_MEM;
+ }
+ rambuf->tot_len = rambuf->len = mtu;
+ rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
+
+ /* Copy the IP header in it */
+ iphdr = (struct ip_hdr *)rambuf->payload;
+ SMEMCPY(iphdr, p->payload, IP_HLEN);
+#else /* IP_FRAG_USES_STATIC_BUF */
+ original_iphdr = (struct ip_hdr *)p->payload;
+ iphdr = original_iphdr;
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+ /* Save original offset */
+ tmp = ntohs(IPH_OFFSET(iphdr));
+ ofo = tmp & IP_OFFMASK;
+ omf = tmp & IP_MF;
+
+ left = p->tot_len - IP_HLEN;
+
+ nfb = (mtu - IP_HLEN) / 8;
+
+ while (left) {
+ last = (left <= mtu - IP_HLEN);
+
+ /* Set new offset and MF flag */
+ tmp = omf | (IP_OFFMASK & (ofo));
+ if (!last) {
+ tmp = tmp | IP_MF;
+ }
+
+ /* Fill this fragment */
+ cop = last ? left : nfb * 8;
+
+#if IP_FRAG_USES_STATIC_BUF
+ poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
+#else /* IP_FRAG_USES_STATIC_BUF */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM);
+ if (rambuf == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+ poff += pbuf_copy_partial(p, rambuf->payload, cop, poff);
+ /* make room for the IP header */
+ if(pbuf_header(rambuf, IP_HLEN)) {
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ /* fill in the IP header */
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = rambuf->payload;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ /* When not using a static buffer, create a chain of pbufs.
+ * The first will be a PBUF_RAM holding the link and IP header.
+ * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+ * but limited to the size of an mtu.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (p->len >= (IP_HLEN)));
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = (struct ip_hdr *)rambuf->payload;
+
+ /* Can just adjust p directly for needed offset. */
+ p->payload = (u8_t *)p->payload + poff;
+ p->len -= poff;
+
+ left_to_copy = cop;
+ while (left_to_copy) {
+ struct pbuf_custom_ref *pcr;
+ newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
+ /* Is this pbuf already empty? */
+ if (!newpbuflen) {
+ p = p->next;
+ continue;
+ }
+ pcr = ip_frag_alloc_pbuf_custom_ref();
+ if (pcr == NULL) {
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ /* Mirror this pbuf, although we might not need all of it. */
+ newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
+ if (newpbuf == NULL) {
+ ip_frag_free_pbuf_custom_ref(pcr);
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ pbuf_ref(p);
+ pcr->original = p;
+ pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
+
+ /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+ * so that it is removed when pbuf_dechain is later called on rambuf.
+ */
+ pbuf_cat(rambuf, newpbuf);
+ left_to_copy -= newpbuflen;
+ if (left_to_copy) {
+ p = p->next;
+ }
+ }
+ poff = newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+ /* Correct header */
+ IPH_OFFSET_SET(iphdr, htons(tmp));
+ IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
+ IPH_CHKSUM_SET(iphdr, 0);
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+
+#if IP_FRAG_USES_STATIC_BUF
+ if (last) {
+ pbuf_realloc(rambuf, left + IP_HLEN);
+ }
+
+ /* This part is ugly: we alloc a RAM based pbuf for
+ * the link level header for each chunk and then
+ * free it.A PBUF_ROM style pbuf for which pbuf_header
+ * worked would make things simpler.
+ */
+ header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
+ if (header != NULL) {
+ pbuf_chain(header, rambuf);
+ netif->output(netif, header, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+ snmp_inc_ipfragcreates();
+ pbuf_free(header);
+ } else {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+#else /* IP_FRAG_USES_STATIC_BUF */
+ /* No need for separate header pbuf - we allowed room for it in rambuf
+ * when allocated.
+ */
+ netif->output(netif, rambuf, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+
+ /* Unfortunately we can't reuse rambuf - the hardware may still be
+ * using the buffer. Instead we free it (and the ensuing chain) and
+ * recreate it next time round the loop. If we're lucky the hardware
+ * will have already sent the packet, the free will really free, and
+ * there will be zero memory penalty.
+ */
+
+ pbuf_free(rambuf);
+#endif /* IP_FRAG_USES_STATIC_BUF */
+ left -= cop;
+ ofo += nfb;
+ }
+#if IP_FRAG_USES_STATIC_BUF
+ pbuf_free(rambuf);
+#endif /* IP_FRAG_USES_STATIC_BUF */
+ snmp_inc_ipfragoks();
+ return ERR_OK;
+}
+#endif /* IP_FRAG */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/mem.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,643 @@
+#pragma diag_remark 177
+/**
+ * @file
+ * Dynamic memory manager
+ *
+ * This is a lightweight replacement for the standard C library malloc().
+ *
+ * If you want to use the standard C library malloc() instead, define
+ * MEM_LIBC_MALLOC to 1 in your lwipopts.h
+ *
+ * To let mem_malloc() use pools (prevents fragmentation and is much faster than
+ * a heap but might waste some memory), define MEM_USE_POOLS to 1, define
+ * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
+ * of pools like this (more pools can be added between _START and _END):
+ *
+ * Define three pools with sizes 256, 512, and 1512 bytes
+ * LWIP_MALLOC_MEMPOOL_START
+ * LWIP_MALLOC_MEMPOOL(20, 256)
+ * LWIP_MALLOC_MEMPOOL(10, 512)
+ * LWIP_MALLOC_MEMPOOL(5, 1512)
+ * LWIP_MALLOC_MEMPOOL_END
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+#include "lwip/err.h"
+
+#include <string.h>
+
+#if MEM_USE_POOLS
+/* lwIP head implemented with different sized pools */
+
+/**
+ * Allocate memory: determine the smallest pool that is big enough
+ * to contain an element of 'size' and get an element from that pool.
+ *
+ * @param size the size in bytes of the memory needed
+ * @return a pointer to the allocated memory or NULL if the pool is empty
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ struct memp_malloc_helper *element;
+ memp_t poolnr;
+ mem_size_t required_size = size + sizeof(struct memp_malloc_helper);
+
+ for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) {
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+again:
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+ /* is this pool big enough to hold an element of the required size
+ plus a struct memp_malloc_helper that saves the pool this element came from? */
+ if (required_size <= memp_sizes[poolnr]) {
+ break;
+ }
+ }
+ if (poolnr > MEMP_POOL_LAST) {
+ LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
+ return NULL;
+ }
+ element = (struct memp_malloc_helper*)memp_malloc(poolnr);
+ if (element == NULL) {
+ /* No need to DEBUGF or ASSERT: This error is already
+ taken care of in memp.c */
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+ /** Try a bigger pool if this one is empty! */
+ if (poolnr < MEMP_POOL_LAST) {
+ poolnr++;
+ goto again;
+ }
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+ return NULL;
+ }
+
+ /* save the pool number this element came from */
+ element->poolnr = poolnr;
+ /* and return a pointer to the memory directly after the struct memp_malloc_helper */
+ element++;
+
+ return element;
+}
+
+/**
+ * Free memory previously allocated by mem_malloc. Loads the pool number
+ * and calls memp_free with that pool number to put the element back into
+ * its pool
+ *
+ * @param rmem the memory element to free
+ */
+void
+mem_free(void *rmem)
+{
+ struct memp_malloc_helper *hmem = (struct memp_malloc_helper*)rmem;
+
+ LWIP_ASSERT("rmem != NULL", (rmem != NULL));
+ LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
+
+ /* get the original struct memp_malloc_helper */
+ hmem--;
+
+ LWIP_ASSERT("hmem != NULL", (hmem != NULL));
+ LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
+ LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
+
+ /* and put it in the pool we saved earlier */
+ memp_free(hmem->poolnr, hmem);
+}
+
+#else /* MEM_USE_POOLS */
+/* lwIP replacement for your libc malloc() */
+
+/**
+ * The heap is made up as a list of structs of this type.
+ * This does not have to be aligned since for getting its size,
+ * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes.
+ */
+struct mem {
+ /** index (-> ram[next]) of the next struct */
+ mem_size_t next;
+ /** index (-> ram[prev]) of the previous struct */
+ mem_size_t prev;
+ /** 1: this area is used; 0: this area is unused */
+ u8_t used;
+};
+
+/** All allocated blocks will be MIN_SIZE bytes big, at least!
+ * MIN_SIZE can be overridden to suit your needs. Smaller values save space,
+ * larger values could prevent too small blocks to fragment the RAM too much. */
+#ifndef MIN_SIZE
+#define MIN_SIZE 12
+#endif /* MIN_SIZE */
+/* some alignment macros: we define them here for better source code layout */
+#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
+#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
+#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
+
+/** If you want to relocate the heap to external memory, simply define
+ * LWIP_RAM_HEAP_POINTER as a void-pointer to that location.
+ * If so, make sure the memory at that location is big enough (see below on
+ * how that space is calculated). */
+#ifndef LWIP_RAM_HEAP_POINTER
+/** the heap. we need one struct mem at the end and some room for alignment */
+u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT] MEM_POSITION;
+#define LWIP_RAM_HEAP_POINTER ram_heap
+#endif /* LWIP_RAM_HEAP_POINTER */
+
+/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */
+static u8_t *ram;
+/** the last entry, always unused! */
+static struct mem *ram_end;
+/** pointer to the lowest free block, this is used for faster search */
+static struct mem *lfree;
+
+/** concurrent access protection */
+static sys_mutex_t mem_mutex;
+
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+
+static volatile u8_t mem_free_count;
+
+/* Allow mem_free from other (e.g. interrupt) context */
+#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free)
+#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free)
+#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free)
+#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc)
+
+#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+/* Protect the heap only by using a semaphore */
+#define LWIP_MEM_FREE_DECL_PROTECT()
+#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex)
+#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex)
+/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */
+#define LWIP_MEM_ALLOC_DECL_PROTECT()
+#define LWIP_MEM_ALLOC_PROTECT()
+#define LWIP_MEM_ALLOC_UNPROTECT()
+
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+
+/**
+ * "Plug holes" by combining adjacent empty struct mems.
+ * After this function is through, there should not exist
+ * one empty struct mem pointing to another empty struct mem.
+ *
+ * @param mem this points to a struct mem which just has been freed
+ * @internal this function is only called by mem_free() and mem_trim()
+ *
+ * This assumes access to the heap is protected by the calling function
+ * already.
+ */
+static void
+plug_holes(struct mem *mem)
+{
+ struct mem *nmem;
+ struct mem *pmem;
+
+ LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram);
+ LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end);
+ LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0);
+
+ /* plug hole forward */
+ LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED);
+
+ nmem = (struct mem *)(void *)&ram[mem->next];
+ if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {
+ /* if mem->next is unused and not end of ram, combine mem and mem->next */
+ if (lfree == nmem) {
+ lfree = mem;
+ }
+ mem->next = nmem->next;
+ ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram);
+ }
+
+ /* plug hole backward */
+ pmem = (struct mem *)(void *)&ram[mem->prev];
+ if (pmem != mem && pmem->used == 0) {
+ /* if mem->prev is unused, combine mem and mem->prev */
+ if (lfree == mem) {
+ lfree = pmem;
+ }
+ pmem->next = mem->next;
+ ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram);
+ }
+}
+
+/**
+ * Zero the heap and initialize start, end and lowest-free
+ */
+void
+mem_init(void)
+{
+ struct mem *mem;
+
+ LWIP_ASSERT("Sanity check alignment",
+ (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);
+
+ /* align the heap */
+ ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
+ /* initialize the start of the heap */
+ mem = (struct mem *)(void *)ram;
+ mem->next = MEM_SIZE_ALIGNED;
+ mem->prev = 0;
+ mem->used = 0;
+ /* initialize the end of the heap */
+ ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED];
+ ram_end->used = 1;
+ ram_end->next = MEM_SIZE_ALIGNED;
+ ram_end->prev = MEM_SIZE_ALIGNED;
+
+ /* initialize the lowest-free pointer to the start of the heap */
+ lfree = (struct mem *)(void *)ram;
+
+ MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
+
+ if(sys_mutex_new(&mem_mutex) != ERR_OK) {
+ LWIP_ASSERT("failed to create mem_mutex", 0);
+ }
+}
+
+/**
+ * Put a struct mem back on the heap
+ *
+ * @param rmem is the data portion of a struct mem as returned by a previous
+ * call to mem_malloc()
+ */
+void
+mem_free(void *rmem)
+{
+ struct mem *mem;
+ LWIP_MEM_FREE_DECL_PROTECT();
+
+ if (rmem == NULL) {
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
+ return;
+ }
+ LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0);
+
+ LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+ (u8_t *)rmem < (u8_t *)ram_end);
+
+ if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
+ /* protect mem stats from concurrent access */
+ SYS_ARCH_PROTECT(lev);
+ MEM_STATS_INC(illegal);
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+ /* protect the heap from concurrent access */
+ LWIP_MEM_FREE_PROTECT();
+ /* Get the corresponding struct mem ... */
+ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+ /* ... which has to be in a used state ... */
+ LWIP_ASSERT("mem_free: mem->used", mem->used);
+ /* ... and is now unused. */
+ mem->used = 0;
+
+ if (mem < lfree) {
+ /* the newly freed struct is now the lowest */
+ lfree = mem;
+ }
+
+ MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram)));
+
+ /* finally, see if prev or next are free also */
+ plug_holes(mem);
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_FREE_UNPROTECT();
+}
+
+/**
+ * Shrink memory returned by mem_malloc().
+ *
+ * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked
+ * @param newsize required size after shrinking (needs to be smaller than or
+ * equal to the previous size)
+ * @return for compatibility reasons: is always == rmem, at the moment
+ * or NULL if newsize is > old size, in which case rmem is NOT touched
+ * or freed!
+ */
+void *
+mem_trim(void *rmem, mem_size_t newsize)
+{
+ mem_size_t size;
+ mem_size_t ptr, ptr2;
+ struct mem *mem, *mem2;
+ /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
+ LWIP_MEM_FREE_DECL_PROTECT();
+
+ /* Expand the size of the allocated memory region so that we can
+ adjust for alignment. */
+ newsize = LWIP_MEM_ALIGN_SIZE(newsize);
+
+ if(newsize < MIN_SIZE_ALIGNED) {
+ /* every data block must be at least MIN_SIZE_ALIGNED long */
+ newsize = MIN_SIZE_ALIGNED;
+ }
+
+ if (newsize > MEM_SIZE_ALIGNED) {
+ return NULL;
+ }
+
+ LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+ (u8_t *)rmem < (u8_t *)ram_end);
+
+ if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
+ /* protect mem stats from concurrent access */
+ SYS_ARCH_PROTECT(lev);
+ MEM_STATS_INC(illegal);
+ SYS_ARCH_UNPROTECT(lev);
+ return rmem;
+ }
+ /* Get the corresponding struct mem ... */
+ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+ /* ... and its offset pointer */
+ ptr = (mem_size_t)((u8_t *)mem - ram);
+
+ size = mem->next - ptr - SIZEOF_STRUCT_MEM;
+ LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
+ if (newsize > size) {
+ /* not supported */
+ return NULL;
+ }
+ if (newsize == size) {
+ /* No change in size, simply return */
+ return rmem;
+ }
+
+ /* protect the heap from concurrent access */
+ LWIP_MEM_FREE_PROTECT();
+
+ mem2 = (struct mem *)(void *)&ram[mem->next];
+ if(mem2->used == 0) {
+ /* The next struct is unused, we can simply move it at little */
+ mem_size_t next;
+ /* remember the old next pointer */
+ next = mem2->next;
+ /* create new struct mem which is moved directly after the shrinked mem */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ if (lfree == mem2) {
+ lfree = (struct mem *)(void *)&ram[ptr2];
+ }
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ mem2->used = 0;
+ /* restore the next pointer */
+ mem2->next = next;
+ /* link it back to mem */
+ mem2->prev = ptr;
+ /* link mem to it */
+ mem->next = ptr2;
+ /* last thing to restore linked list: as we have moved mem2,
+ * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not
+ * the end of the heap */
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_DEC_USED(used, (size - newsize));
+ /* no need to plug holes, we've already done that */
+ } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {
+ /* Next struct is used but there's room for another struct mem with
+ * at least MIN_SIZE_ALIGNED of data.
+ * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem
+ * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED').
+ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+ * region that couldn't hold data, but when mem->next gets freed,
+ * the 2 regions would be combined, resulting in more free memory */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ if (mem2 < lfree) {
+ lfree = mem2;
+ }
+ mem2->used = 0;
+ mem2->next = mem->next;
+ mem2->prev = ptr;
+ mem->next = ptr2;
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_DEC_USED(used, (size - newsize));
+ /* the original mem->next is used, so no need to plug holes! */
+ }
+ /* else {
+ next struct mem is used but size between mem and mem2 is not big enough
+ to create another struct mem
+ -> don't do anyhting.
+ -> the remaining space stays unused since it is too small
+ } */
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_FREE_UNPROTECT();
+ return rmem;
+}
+
+/**
+ * Adam's mem_malloc() plus solution for bug #17922
+ * Allocate a block of memory with a minimum of 'size' bytes.
+ *
+ * @param size is the minimum size of the requested block in bytes.
+ * @return pointer to allocated memory or NULL if no free memory was found.
+ *
+ * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ mem_size_t ptr, ptr2;
+ struct mem *mem, *mem2;
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ u8_t local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_ALLOC_DECL_PROTECT();
+
+ if (size == 0) {
+ return NULL;
+ }
+
+ /* Expand the size of the allocated memory region so that we can
+ adjust for alignment. */
+ size = LWIP_MEM_ALIGN_SIZE(size);
+
+ if(size < MIN_SIZE_ALIGNED) {
+ /* every data block must be at least MIN_SIZE_ALIGNED long */
+ size = MIN_SIZE_ALIGNED;
+ }
+
+ if (size > MEM_SIZE_ALIGNED) {
+ return NULL;
+ }
+
+ /* protect the heap from concurrent access */
+ sys_mutex_lock(&mem_mutex);
+ LWIP_MEM_ALLOC_PROTECT();
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ /* run as long as a mem_free disturbed mem_malloc */
+ do {
+ local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+ /* Scan through the heap searching for a free block that is big enough,
+ * beginning with the lowest free block.
+ */
+ for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size;
+ ptr = ((struct mem *)(void *)&ram[ptr])->next) {
+ mem = (struct mem *)(void *)&ram[ptr];
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 0;
+ LWIP_MEM_ALLOC_UNPROTECT();
+ /* allow mem_free to run */
+ LWIP_MEM_ALLOC_PROTECT();
+ if (mem_free_count != 0) {
+ local_mem_free_count = mem_free_count;
+ }
+ mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+ if ((!mem->used) &&
+ (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
+ /* mem is not used and at least perfect fit is possible:
+ * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
+
+ if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {
+ /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
+ * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
+ * -> split large block, create empty remainder,
+ * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
+ * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
+ * struct mem would fit in but no data between mem2 and mem2->next
+ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+ * region that couldn't hold data, but when mem->next gets freed,
+ * the 2 regions would be combined, resulting in more free memory
+ */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
+ /* create mem2 struct */
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ mem2->used = 0;
+ mem2->next = mem->next;
+ mem2->prev = ptr;
+ /* and insert it between mem and mem->next */
+ mem->next = ptr2;
+ mem->used = 1;
+
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
+ } else {
+ /* (a mem2 struct does no fit into the user data space of mem and mem->next will always
+ * be used at this point: if not we have 2 unused structs in a row, plug_holes should have
+ * take care of this).
+ * -> near fit or excact fit: do not split, no mem2 creation
+ * also can't move mem->next directly behind mem, since mem->next
+ * will always be used at this point!
+ */
+ mem->used = 1;
+ MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram));
+ }
+
+ if (mem == lfree) {
+ /* Find next free block after mem and update lowest free pointer */
+ while (lfree->used && lfree != ram_end) {
+ LWIP_MEM_ALLOC_UNPROTECT();
+ /* prevent high interrupt latency... */
+ LWIP_MEM_ALLOC_PROTECT();
+ lfree = (struct mem *)(void *)&ram[lfree->next];
+ }
+ LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
+ }
+ LWIP_MEM_ALLOC_UNPROTECT();
+ sys_mutex_unlock(&mem_mutex);
+ LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
+ (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
+ LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
+ ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
+ LWIP_ASSERT("mem_malloc: sanity check alignment",
+ (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0);
+
+ return (u8_t *)mem + SIZEOF_STRUCT_MEM;
+ }
+ }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ /* if we got interrupted by a mem_free, try again */
+ } while(local_mem_free_count != 0);
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
+ MEM_STATS_INC(err);
+ LWIP_MEM_ALLOC_UNPROTECT();
+ sys_mutex_unlock(&mem_mutex);
+ return NULL;
+}
+
+#endif /* MEM_USE_POOLS */
+/**
+ * Contiguously allocates enough space for count objects that are size bytes
+ * of memory each and returns a pointer to the allocated memory.
+ *
+ * The allocated memory is filled with bytes of value zero.
+ *
+ * @param count number of objects to allocate
+ * @param size size of the objects to allocate
+ * @return pointer to allocated memory / NULL pointer if there is an error
+ */
+void *mem_calloc(mem_size_t count, mem_size_t size)
+{
+ void *p;
+
+ /* allocate 'count' objects of size 'size' */
+ p = mem_malloc(count * size);
+ if (p) {
+ /* zero the memory */
+ memset(p, 0, count * size);
+ }
+ return p;
+}
+
+#endif /* !MEM_LIBC_MALLOC */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/memp.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,469 @@
+/**
+ * @file
+ * Dynamic pool memory manager
+ *
+ * lwIP has dedicated pools for many structures (netconn, protocol control blocks,
+ * packet buffers, ...). All these pools are managed here.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/udp.h"
+#include "lwip/raw.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/igmp.h"
+#include "lwip/api.h"
+#include "lwip/api_msg.h"
+#include "lwip/tcpip.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+#include "lwip/stats.h"
+#include "netif/etharp.h"
+#include "lwip/ip_frag.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/dns.h"
+#include "netif/ppp_oe.h"
+
+#include <string.h>
+
+#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+struct memp {
+ struct memp *next;
+#if MEMP_OVERFLOW_CHECK
+ const char *file;
+ int line;
+#endif /* MEMP_OVERFLOW_CHECK */
+};
+
+#if MEMP_OVERFLOW_CHECK
+/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning
+ * and at the end of each element, initialize them as 0xcd and check
+ * them later. */
+/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free,
+ * every single element in each pool is checked!
+ * This is VERY SLOW but also very helpful. */
+/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in
+ * lwipopts.h to change the amount reserved for checking. */
+#ifndef MEMP_SANITY_REGION_BEFORE
+#define MEMP_SANITY_REGION_BEFORE 16
+#endif /* MEMP_SANITY_REGION_BEFORE*/
+#if MEMP_SANITY_REGION_BEFORE > 0
+#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE)
+#else
+#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0
+#endif /* MEMP_SANITY_REGION_BEFORE*/
+#ifndef MEMP_SANITY_REGION_AFTER
+#define MEMP_SANITY_REGION_AFTER 16
+#endif /* MEMP_SANITY_REGION_AFTER*/
+#if MEMP_SANITY_REGION_AFTER > 0
+#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER)
+#else
+#define MEMP_SANITY_REGION_AFTER_ALIGNED 0
+#endif /* MEMP_SANITY_REGION_AFTER*/
+
+/* MEMP_SIZE: save space for struct memp and for sanity check */
+#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED)
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED)
+
+#else /* MEMP_OVERFLOW_CHECK */
+
+/* No sanity checks
+ * We don't need to preserve the struct memp while not allocated, so we
+ * can save a little space and set MEMP_SIZE to 0.
+ */
+#define MEMP_SIZE 0
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
+
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/** This array holds the first free element of each pool.
+ * Elements form a linked list. */
+static struct memp *memp_tab[MEMP_MAX] MEM_POSITION;
+
+#else /* MEMP_MEM_MALLOC */
+
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
+
+#endif /* MEMP_MEM_MALLOC */
+
+/** This array holds the element sizes of each pool. */
+#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC
+static
+#endif
+const u16_t memp_sizes[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size),
+#include "lwip/memp_std.h"
+};
+
+#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+/** This array holds the number of elements in each pool. */
+static const u16_t memp_num[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) (num),
+#include "lwip/memp_std.h"
+};
+
+/** This array holds a textual description of each pool. */
+#ifdef LWIP_DEBUG
+static const char *memp_desc[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) (desc),
+#include "lwip/memp_std.h"
+};
+#endif /* LWIP_DEBUG */
+
+#if MEMP_SEPARATE_POOLS
+
+/** This creates each memory pool. These are named memp_memory_XXX_base (where
+ * XXX is the name of the pool defined in memp_std.h).
+ * To relocate a pool, declare it as extern in cc.h. Example for GCC:
+ * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[];
+ */
+#define LWIP_MEMPOOL(name,num,size,desc) u8_t memp_memory_ ## name ## _base \
+ [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))];
+#include "lwip/memp_std.h"
+
+/** This array holds the base of each memory pool. */
+static u8_t *const memp_bases[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) memp_memory_ ## name ## _base,
+#include "lwip/memp_std.h"
+} MEM_POSITION;
+
+#else /* MEMP_SEPARATE_POOLS */
+
+/** This is the actual memory used by the pools (all pools in one big block). */
+static u8_t memp_memory[MEM_ALIGNMENT - 1
+#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
+#include "lwip/memp_std.h"
+] MEM_POSITION;
+
+#endif /* MEMP_SEPARATE_POOLS */
+
+#if MEMP_SANITY_CHECK
+/**
+ * Check that memp-lists don't form a circle
+ */
+static int
+memp_sanity(void)
+{
+ s16_t i, c;
+ struct memp *m, *n;
+
+ for (i = 0; i < MEMP_MAX; i++) {
+ for (m = memp_tab[i]; m != NULL; m = m->next) {
+ c = 1;
+ for (n = memp_tab[i]; n != NULL; n = n->next) {
+ if (n == m && --c < 0) {
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+}
+#endif /* MEMP_SANITY_CHECK*/
+#if MEMP_OVERFLOW_CHECK
+#if defined(LWIP_DEBUG) && MEMP_STATS
+static const char * memp_overflow_names[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) "/"desc,
+#include "lwip/memp_std.h"
+ };
+#endif
+
+/**
+ * Check if a memp element was victim of an overflow
+ * (e.g. the restricted area after it has been altered)
+ *
+ * @param p the memp element to check
+ * @param memp_type the pool p comes from
+ */
+static void
+memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type)
+{
+ u16_t k;
+ u8_t *m;
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type];
+ for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) {
+ if (m[k] != 0xcd) {
+ char errstr[128] = "detected memp overflow in pool ";
+ char digit[] = "0";
+ if(memp_type >= 10) {
+ digit[0] = '0' + (memp_type/10);
+ strcat(errstr, digit);
+ }
+ digit[0] = '0' + (memp_type%10);
+ strcat(errstr, digit);
+#if defined(LWIP_DEBUG) && MEMP_STATS
+ strcat(errstr, memp_overflow_names[memp_type]);
+#endif
+ LWIP_ASSERT(errstr, 0);
+ }
+ }
+#endif
+}
+
+/**
+ * Check if a memp element was victim of an underflow
+ * (e.g. the restricted area before it has been altered)
+ *
+ * @param p the memp element to check
+ * @param memp_type the pool p comes from
+ */
+static void
+memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type)
+{
+ u16_t k;
+ u8_t *m;
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) {
+ if (m[k] != 0xcd) {
+ char errstr[128] = "detected memp underflow in pool ";
+ char digit[] = "0";
+ if(memp_type >= 10) {
+ digit[0] = '0' + (memp_type/10);
+ strcat(errstr, digit);
+ }
+ digit[0] = '0' + (memp_type%10);
+ strcat(errstr, digit);
+#if defined(LWIP_DEBUG) && MEMP_STATS
+ strcat(errstr, memp_overflow_names[memp_type]);
+#endif
+ LWIP_ASSERT(errstr, 0);
+ }
+ }
+#endif
+}
+
+/**
+ * Do an overflow check for all elements in every pool.
+ *
+ * @see memp_overflow_check_element for a description of the check
+ */
+static void
+memp_overflow_check_all(void)
+{
+ u16_t i, j;
+ struct memp *p;
+
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+ for (i = 0; i < MEMP_MAX; ++i) {
+ p = p;
+ for (j = 0; j < memp_num[i]; ++j) {
+ memp_overflow_check_element_overflow(p, i);
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+ for (i = 0; i < MEMP_MAX; ++i) {
+ p = p;
+ for (j = 0; j < memp_num[i]; ++j) {
+ memp_overflow_check_element_underflow(p, i);
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+}
+
+/**
+ * Initialize the restricted areas of all memp elements in every pool.
+ */
+static void
+memp_overflow_init(void)
+{
+ u16_t i, j;
+ struct memp *p;
+ u8_t *m;
+
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+ for (i = 0; i < MEMP_MAX; ++i) {
+ p = p;
+ for (j = 0; j < memp_num[i]; ++j) {
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED);
+#endif
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE + memp_sizes[i];
+ memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED);
+#endif
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+}
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/**
+ * Initialize this module.
+ *
+ * Carves out memp_memory into linked lists for each pool-type.
+ */
+void
+memp_init(void)
+{
+ struct memp *memp;
+ u16_t i, j;
+
+ for (i = 0; i < MEMP_MAX; ++i) {
+ MEMP_STATS_AVAIL(used, i, 0);
+ MEMP_STATS_AVAIL(max, i, 0);
+ MEMP_STATS_AVAIL(err, i, 0);
+ MEMP_STATS_AVAIL(avail, i, memp_num[i]);
+ }
+
+#if !MEMP_SEPARATE_POOLS
+ memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+#endif /* !MEMP_SEPARATE_POOLS */
+ /* for every pool: */
+ for (i = 0; i < MEMP_MAX; ++i) {
+ memp_tab[i] = NULL;
+#if MEMP_SEPARATE_POOLS
+ memp = (struct memp*)memp_bases[i];
+#endif /* MEMP_SEPARATE_POOLS */
+ /* create a linked list of memp elements */
+ for (j = 0; j < memp_num[i]; ++j) {
+ memp->next = memp_tab[i];
+ memp_tab[i] = memp;
+ memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]
+#if MEMP_OVERFLOW_CHECK
+ + MEMP_SANITY_REGION_AFTER_ALIGNED
+#endif
+ );
+ }
+ }
+#if MEMP_OVERFLOW_CHECK
+ memp_overflow_init();
+ /* check everything a first time to see if it worked */
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK */
+}
+
+/**
+ * Get an element from a specific pool.
+ *
+ * @param type the pool to get an element from
+ *
+ * the debug version has two more parameters:
+ * @param file file name calling this function
+ * @param line number of line where this function is called
+ *
+ * @return a pointer to the allocated memory or a NULL pointer on error
+ */
+void *
+#if !MEMP_OVERFLOW_CHECK
+memp_malloc(memp_t type)
+#else
+memp_malloc_fn(memp_t type, const char* file, const int line)
+#endif
+{
+ struct memp *memp;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
+
+ SYS_ARCH_PROTECT(old_level);
+#if MEMP_OVERFLOW_CHECK >= 2
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+
+ memp = memp_tab[type];
+
+ if (memp != NULL) {
+ memp_tab[type] = memp->next;
+#if MEMP_OVERFLOW_CHECK
+ memp->next = NULL;
+ memp->file = file;
+ memp->line = line;
+#endif /* MEMP_OVERFLOW_CHECK */
+ MEMP_STATS_INC_USED(used, type);
+ LWIP_ASSERT("memp_malloc: memp properly aligned",
+ ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
+ memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE);
+ } else {
+ LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type]));
+ MEMP_STATS_INC(err, type);
+ }
+
+ SYS_ARCH_UNPROTECT(old_level);
+
+ return memp;
+}
+
+/**
+ * Put an element back into its pool.
+ *
+ * @param type the pool where to put mem
+ * @param mem the memp element to free
+ */
+void
+memp_free(memp_t type, void *mem)
+{
+ struct memp *memp;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ if (mem == NULL) {
+ return;
+ }
+ LWIP_ASSERT("memp_free: mem properly aligned",
+ ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
+
+ memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
+
+ SYS_ARCH_PROTECT(old_level);
+#if MEMP_OVERFLOW_CHECK
+#if MEMP_OVERFLOW_CHECK >= 2
+ memp_overflow_check_all();
+#else
+ memp_overflow_check_element_overflow(memp, type);
+ memp_overflow_check_element_underflow(memp, type);
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+#endif /* MEMP_OVERFLOW_CHECK */
+
+ MEMP_STATS_DEC(used, type);
+
+ memp->next = memp_tab[type];
+ memp_tab[type] = memp;
+
+#if MEMP_SANITY_CHECK
+ LWIP_ASSERT("memp sanity", memp_sanity());
+#endif /* MEMP_SANITY_CHECK */
+
+ SYS_ARCH_UNPROTECT(old_level);
+}
+
+#endif /* MEMP_MEM_MALLOC */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/netif.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,752 @@
+/**
+ * @file
+ * lwIP network interface abstraction
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp.h"
+#include "lwip/igmp.h"
+#include "netif/etharp.h"
+#include "lwip/stats.h"
+#if ENABLE_LOOPBACK
+#include "lwip/sys.h"
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+#include "lwip/tcpip.h"
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#if LWIP_AUTOIP
+#include "lwip/autoip.h"
+#endif /* LWIP_AUTOIP */
+#if LWIP_DHCP
+#include "lwip/dhcp.h"
+#endif /* LWIP_DHCP */
+
+#if LWIP_NETIF_STATUS_CALLBACK
+#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0)
+#else
+#define NETIF_STATUS_CALLBACK(n)
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_LINK_CALLBACK
+#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0)
+#else
+#define NETIF_LINK_CALLBACK(n)
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+struct netif *netif_list;
+struct netif *netif_default;
+
+#if LWIP_HAVE_LOOPIF
+static struct netif loop_netif;
+
+/**
+ * Initialize a lwip network interface structure for a loopback interface
+ *
+ * @param netif the lwip network interface structure for this loopif
+ * @return ERR_OK if the loopif is initialized
+ * ERR_MEM if private data couldn't be allocated
+ */
+static err_t
+netif_loopif_init(struct netif *netif)
+{
+ /* initialize the snmp variables and counters inside the struct netif
+ * ifSpeed: no assumption can be made!
+ */
+ NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0);
+
+ netif->name[0] = 'l';
+ netif->name[1] = 'o';
+ netif->output = netif_loop_output;
+ return ERR_OK;
+}
+#endif /* LWIP_HAVE_LOOPIF */
+
+void
+netif_init(void)
+{
+#if LWIP_HAVE_LOOPIF
+ ip_addr_t loop_ipaddr, loop_netmask, loop_gw;
+ IP4_ADDR(&loop_gw, 127,0,0,1);
+ IP4_ADDR(&loop_ipaddr, 127,0,0,1);
+ IP4_ADDR(&loop_netmask, 255,0,0,0);
+
+#if NO_SYS
+ netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input);
+#else /* NO_SYS */
+ netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input);
+#endif /* NO_SYS */
+ netif_set_up(&loop_netif);
+
+#endif /* LWIP_HAVE_LOOPIF */
+}
+
+/**
+ * Add a network interface to the list of lwIP netifs.
+ *
+ * @param netif a pre-allocated netif structure
+ * @param ipaddr IP address for the new netif
+ * @param netmask network mask for the new netif
+ * @param gw default gateway IP address for the new netif
+ * @param state opaque data passed to the new netif
+ * @param init callback function that initializes the interface
+ * @param input callback function that is called to pass
+ * ingress packets up in the protocol layer stack.
+ *
+ * @return netif, or NULL if failed.
+ */
+struct netif *
+netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input)
+{
+ static u8_t netifnum = 0;
+
+ LWIP_ASSERT("No init function given", init != NULL);
+
+ /* reset new interface configuration state */
+ ip_addr_set_zero(&netif->ip_addr);
+ ip_addr_set_zero(&netif->netmask);
+ ip_addr_set_zero(&netif->gw);
+ netif->flags = 0;
+#if LWIP_DHCP
+ /* netif not under DHCP control by default */
+ netif->dhcp = NULL;
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+ /* netif not under AutoIP control by default */
+ netif->autoip = NULL;
+#endif /* LWIP_AUTOIP */
+#if LWIP_NETIF_STATUS_CALLBACK
+ netif->status_callback = NULL;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+ netif->link_callback = NULL;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_IGMP
+ netif->igmp_mac_filter = NULL;
+#endif /* LWIP_IGMP */
+#if ENABLE_LOOPBACK
+ netif->loop_first = NULL;
+ netif->loop_last = NULL;
+#endif /* ENABLE_LOOPBACK */
+
+ /* remember netif specific state information data */
+ netif->state = state;
+ netif->num = netifnum++;
+ netif->input = input;
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = NULL;
+#endif /* LWIP_NETIF_HWADDRHINT*/
+#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
+ netif->loop_cnt_current = 0;
+#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */
+
+ netif_set_addr(netif, ipaddr, netmask, gw);
+
+ /* call user specified initialization function for netif */
+ if (init(netif) != ERR_OK) {
+ return NULL;
+ }
+
+ /* add this netif to the list */
+ netif->next = netif_list;
+ netif_list = netif;
+ snmp_inc_iflist();
+
+#if LWIP_IGMP
+ /* start IGMP processing */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_start(netif);
+ }
+#endif /* LWIP_IGMP */
+
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ",
+ netif->name[0], netif->name[1]));
+ ip_addr_debug_print(NETIF_DEBUG, ipaddr);
+ LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
+ ip_addr_debug_print(NETIF_DEBUG, netmask);
+ LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
+ ip_addr_debug_print(NETIF_DEBUG, gw);
+ LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
+ return netif;
+}
+
+/**
+ * Change IP address configuration for a network interface (including netmask
+ * and default gateway).
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ * @param netmask the new netmask
+ * @param gw the new default gateway
+ */
+void
+netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw)
+{
+ netif_set_ipaddr(netif, ipaddr);
+ netif_set_netmask(netif, netmask);
+ netif_set_gw(netif, gw);
+}
+
+/**
+ * Remove a network interface from the list of lwIP netifs.
+ *
+ * @param netif the network interface to remove
+ */
+void
+netif_remove(struct netif *netif)
+{
+ if (netif == NULL) {
+ return;
+ }
+
+#if LWIP_IGMP
+ /* stop IGMP processing */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_stop(netif);
+ }
+#endif /* LWIP_IGMP */
+ if (netif_is_up(netif)) {
+ /* set netif down before removing (call callback function) */
+ netif_set_down(netif);
+ }
+
+ snmp_delete_ipaddridx_tree(netif);
+
+ /* is it the first netif? */
+ if (netif_list == netif) {
+ netif_list = netif->next;
+ } else {
+ /* look for netif further down the list */
+ struct netif * tmpNetif;
+ for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) {
+ if (tmpNetif->next == netif) {
+ tmpNetif->next = netif->next;
+ break;
+ }
+ }
+ if (tmpNetif == NULL)
+ return; /* we didn't find any netif today */
+ }
+ snmp_dec_iflist();
+ /* this netif is default? */
+ if (netif_default == netif) {
+ /* reset default netif */
+ netif_set_default(NULL);
+ }
+ LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
+}
+
+/**
+ * Find a network interface by searching for its name
+ *
+ * @param name the name of the netif (like netif->name) plus concatenated number
+ * in ascii representation (e.g. 'en0')
+ */
+struct netif *
+netif_find(char *name)
+{
+ struct netif *netif;
+ u8_t num;
+
+ if (name == NULL) {
+ return NULL;
+ }
+
+ num = name[2] - '0';
+
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ if (num == netif->num &&
+ name[0] == netif->name[0] &&
+ name[1] == netif->name[1]) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1]));
+ return netif;
+ }
+ }
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1]));
+ return NULL;
+}
+
+/**
+ * Change the IP address of a network interface
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ *
+ * @note call netif_set_addr() if you also want to change netmask and
+ * default gateway
+ */
+void
+netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr)
+{
+ /* TODO: Handling of obsolete pcbs */
+ /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */
+#if LWIP_TCP
+ struct tcp_pcb *pcb;
+ struct tcp_pcb_listen *lpcb;
+
+ /* address is actually being changed? */
+ if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) {
+ /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
+ pcb = tcp_active_pcbs;
+ while (pcb != NULL) {
+ /* PCB bound to current local interface address? */
+ if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */
+ && !ip_addr_islinklocal(&(pcb->local_ip))
+#endif /* LWIP_AUTOIP */
+ ) {
+ /* this connection must be aborted */
+ struct tcp_pcb *next = pcb->next;
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
+ tcp_abort(pcb);
+ pcb = next;
+ } else {
+ pcb = pcb->next;
+ }
+ }
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ /* PCB bound to current local interface address? */
+ if ((!(ip_addr_isany(&(lpcb->local_ip)))) &&
+ (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) {
+ /* The PCB is listening to the old ipaddr and
+ * is set to listen to the new one instead */
+ ip_addr_set(&(lpcb->local_ip), ipaddr);
+ }
+ }
+ }
+#endif
+ snmp_delete_ipaddridx_tree(netif);
+ snmp_delete_iprteidx_tree(0,netif);
+ /* set new IP address to netif */
+ ip_addr_set(&(netif->ip_addr), ipaddr);
+ snmp_insert_ipaddridx_tree(netif);
+ snmp_insert_iprteidx_tree(0,netif);
+
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->ip_addr),
+ ip4_addr2_16(&netif->ip_addr),
+ ip4_addr3_16(&netif->ip_addr),
+ ip4_addr4_16(&netif->ip_addr)));
+}
+
+/**
+ * Change the default gateway for a network interface
+ *
+ * @param netif the network interface to change
+ * @param gw the new default gateway
+ *
+ * @note call netif_set_addr() if you also want to change ip address and netmask
+ */
+void
+netif_set_gw(struct netif *netif, ip_addr_t *gw)
+{
+ ip_addr_set(&(netif->gw), gw);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->gw),
+ ip4_addr2_16(&netif->gw),
+ ip4_addr3_16(&netif->gw),
+ ip4_addr4_16(&netif->gw)));
+}
+
+/**
+ * Change the netmask of a network interface
+ *
+ * @param netif the network interface to change
+ * @param netmask the new netmask
+ *
+ * @note call netif_set_addr() if you also want to change ip address and
+ * default gateway
+ */
+void
+netif_set_netmask(struct netif *netif, ip_addr_t *netmask)
+{
+ snmp_delete_iprteidx_tree(0, netif);
+ /* set new netmask to netif */
+ ip_addr_set(&(netif->netmask), netmask);
+ snmp_insert_iprteidx_tree(0, netif);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->netmask),
+ ip4_addr2_16(&netif->netmask),
+ ip4_addr3_16(&netif->netmask),
+ ip4_addr4_16(&netif->netmask)));
+}
+
+/**
+ * Set a network interface as the default network interface
+ * (used to output all packets for which no specific route is found)
+ *
+ * @param netif the default network interface
+ */
+void
+netif_set_default(struct netif *netif)
+{
+ if (netif == NULL) {
+ /* remove default route */
+ snmp_delete_iprteidx_tree(1, netif);
+ } else {
+ /* install default route */
+ snmp_insert_iprteidx_tree(1, netif);
+ }
+ netif_default = netif;
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n",
+ netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\''));
+}
+
+/**
+ * Bring an interface up, available for processing
+ * traffic.
+ *
+ * @note: Enabling DHCP on a down interface will make it come
+ * up once configured.
+ *
+ * @see dhcp_start()
+ */
+void netif_set_up(struct netif *netif)
+{
+ if (!(netif->flags & NETIF_FLAG_UP)) {
+ netif->flags |= NETIF_FLAG_UP;
+
+#if LWIP_SNMP
+ snmp_get_sysuptime(&netif->ts);
+#endif /* LWIP_SNMP */
+
+ NETIF_STATUS_CALLBACK(netif);
+
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+#if LWIP_ARP
+ /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+ if (netif->flags & (NETIF_FLAG_ETHARP)) {
+ etharp_gratuitous(netif);
+ }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+ /* resend IGMP memberships */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_report_groups( netif);
+ }
+#endif /* LWIP_IGMP */
+ }
+ }
+}
+
+/**
+ * Bring an interface down, disabling any traffic processing.
+ *
+ * @note: Enabling DHCP on a down interface will make it come
+ * up once configured.
+ *
+ * @see dhcp_start()
+ */
+void netif_set_down(struct netif *netif)
+{
+ if (netif->flags & NETIF_FLAG_UP) {
+ netif->flags &= ~NETIF_FLAG_UP;
+#if LWIP_SNMP
+ snmp_get_sysuptime(&netif->ts);
+#endif
+
+ NETIF_STATUS_CALLBACK(netif);
+ }
+}
+
+#if LWIP_NETIF_STATUS_CALLBACK
+/**
+ * Set callback to be called when interface is brought up/down
+ */
+void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback)
+{
+ if (netif) {
+ netif->status_callback = status_callback;
+ }
+}
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+/**
+ * Called by a driver when its link goes up
+ */
+void netif_set_link_up(struct netif *netif )
+{
+ if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
+ netif->flags |= NETIF_FLAG_LINK_UP;
+
+#if LWIP_DHCP
+ if (netif->dhcp) {
+ dhcp_network_changed(netif);
+ }
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+ if (netif->autoip) {
+ autoip_network_changed(netif);
+ }
+#endif /* LWIP_AUTOIP */
+
+ if (netif->flags & NETIF_FLAG_UP) {
+#if LWIP_ARP
+ /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+ if (netif->flags & NETIF_FLAG_ETHARP) {
+ etharp_gratuitous(netif);
+ }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+ /* resend IGMP memberships */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_report_groups( netif);
+ }
+#endif /* LWIP_IGMP */
+ }
+ NETIF_LINK_CALLBACK(netif);
+ }
+}
+
+/**
+ * Called by a driver when its link goes down
+ */
+void netif_set_link_down(struct netif *netif )
+{
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+ netif->flags &= ~NETIF_FLAG_LINK_UP;
+ NETIF_LINK_CALLBACK(netif);
+ }
+}
+
+#if LWIP_NETIF_LINK_CALLBACK
+/**
+ * Set callback to be called when link is brought up/down
+ */
+void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback)
+{
+ if (netif) {
+ netif->link_callback = link_callback;
+ }
+}
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if ENABLE_LOOPBACK
+/**
+ * Send an IP packet to be received on the same netif (loopif-like).
+ * The pbuf is simply copied and handed back to netif->input.
+ * In multithreaded mode, this is done directly since netif->input must put
+ * the packet on a queue.
+ * In callback mode, the packet is put on an internal queue and is fed to
+ * netif->input by netif_poll().
+ *
+ * @param netif the lwip network interface structure
+ * @param p the (IP) packet to 'send'
+ * @param ipaddr the ip address to send the packet to (not used)
+ * @return ERR_OK if the packet has been sent
+ * ERR_MEM if the pbuf used to copy the packet couldn't be allocated
+ */
+err_t
+netif_loop_output(struct netif *netif, struct pbuf *p,
+ ip_addr_t *ipaddr)
+{
+ struct pbuf *r;
+ err_t err;
+ struct pbuf *last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u8_t clen = 0;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+ /* If we have a loopif, SNMP counters are adjusted for it,
+ * if not they are adjusted for 'netif'. */
+#if LWIP_SNMP
+#if LWIP_HAVE_LOOPIF
+ struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+ struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* LWIP_SNMP */
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_UNUSED_ARG(ipaddr);
+
+ /* Allocate a new pbuf */
+ r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return ERR_MEM;
+ }
+#if LWIP_LOOPBACK_MAX_PBUFS
+ clen = pbuf_clen(r);
+ /* check for overflow or too many pbuf on queue */
+ if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
+ ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) {
+ pbuf_free(r);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return ERR_MEM;
+ }
+ netif->loop_cnt_current += clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+ /* Copy the whole pbuf queue p into the single pbuf r */
+ if ((err = pbuf_copy(r, p)) != ERR_OK) {
+ pbuf_free(r);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return err;
+ }
+
+ /* Put the packet on a linked list which gets emptied through calling
+ netif_poll(). */
+
+ /* let last point to the last pbuf in chain r */
+ for (last = r; last->next != NULL; last = last->next);
+
+ SYS_ARCH_PROTECT(lev);
+ if(netif->loop_first != NULL) {
+ LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL);
+ netif->loop_last->next = r;
+ netif->loop_last = last;
+ } else {
+ netif->loop_first = r;
+ netif->loop_last = last;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+
+ LINK_STATS_INC(link.xmit);
+ snmp_add_ifoutoctets(stats_if, p->tot_len);
+ snmp_inc_ifoutucastpkts(stats_if);
+
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+ /* For multithreading environment, schedule a call to netif_poll */
+ tcpip_callback((tcpip_callback_fn)netif_poll, netif);
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+
+ return ERR_OK;
+}
+
+/**
+ * Call netif_poll() in the main loop of your application. This is to prevent
+ * reentering non-reentrant functions like tcp_input(). Packets passed to
+ * netif_loop_output() are put on a list that is passed to netif->input() by
+ * netif_poll().
+ */
+void
+netif_poll(struct netif *netif)
+{
+ struct pbuf *in;
+ /* If we have a loopif, SNMP counters are adjusted for it,
+ * if not they are adjusted for 'netif'. */
+#if LWIP_SNMP
+#if LWIP_HAVE_LOOPIF
+ struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+ struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* LWIP_SNMP */
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ do {
+ /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
+ SYS_ARCH_PROTECT(lev);
+ in = netif->loop_first;
+ if (in != NULL) {
+ struct pbuf *in_end = in;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u8_t clen = pbuf_clen(in);
+ /* adjust the number of pbufs on queue */
+ LWIP_ASSERT("netif->loop_cnt_current underflow",
+ ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
+ netif->loop_cnt_current -= clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+ while (in_end->len != in_end->tot_len) {
+ LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
+ in_end = in_end->next;
+ }
+ /* 'in_end' now points to the last pbuf from 'in' */
+ if (in_end == netif->loop_last) {
+ /* this was the last pbuf in the list */
+ netif->loop_first = netif->loop_last = NULL;
+ } else {
+ /* pop the pbuf off the list */
+ netif->loop_first = in_end->next;
+ LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
+ }
+ /* De-queue the pbuf from its successors on the 'loop_' list. */
+ in_end->next = NULL;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+
+ if (in != NULL) {
+ LINK_STATS_INC(link.recv);
+ snmp_add_ifinoctets(stats_if, in->tot_len);
+ snmp_inc_ifinucastpkts(stats_if);
+ /* loopback packets are always IP packets! */
+ if (ip_input(in, netif) != ERR_OK) {
+ pbuf_free(in);
+ }
+ /* Don't reference the packet any more! */
+ in = NULL;
+ }
+ /* go on while there is a packet on the list */
+ } while (netif->loop_first != NULL);
+}
+
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+/**
+ * Calls netif_poll() for every netif on the netif_list.
+ */
+void
+netif_poll_all(void)
+{
+ struct netif *netif = netif_list;
+ /* loop through netifs */
+ while (netif != NULL) {
+ netif_poll(netif);
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/pbuf.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,1156 @@
+/**
+ * @file
+ * Packet buffer management
+ *
+ * Packets are built from the pbuf data structure. It supports dynamic
+ * memory allocation for packet contents or can reference externally
+ * managed packet contents both in RAM and ROM. Quick allocation for
+ * incoming packets is provided through pools with fixed sized pbufs.
+ *
+ * A packet may span over multiple pbufs, chained as a singly linked
+ * list. This is called a "pbuf chain".
+ *
+ * Multiple packets may be queued, also using this singly linked list.
+ * This is called a "packet queue".
+ *
+ * So, a packet queue consists of one or more pbuf chains, each of
+ * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE
+ * NOT SUPPORTED!!! Use helper structs to queue multiple packets.
+ *
+ * The differences between a pbuf chain and a packet queue are very
+ * precise but subtle.
+ *
+ * The last pbuf of a packet has a ->tot_len field that equals the
+ * ->len field. It can be found by traversing the list. If the last
+ * pbuf of a packet has a ->next field other than NULL, more packets
+ * are on the queue.
+ *
+ * Therefore, looping through a pbuf of a single packet, has an
+ * loop end condition (tot_len == p->len), NOT (next == NULL).
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/stats.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include "arch/perf.h"
+#if TCP_QUEUE_OOSEQ
+#include "lwip/tcp_impl.h"
+#endif
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
+/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically
+ aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */
+#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
+
+#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS
+#define PBUF_POOL_IS_EMPTY()
+#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */
+/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */
+#ifndef PBUF_POOL_FREE_OOSEQ
+#define PBUF_POOL_FREE_OOSEQ 1
+#endif /* PBUF_POOL_FREE_OOSEQ */
+
+#if PBUF_POOL_FREE_OOSEQ
+#include "lwip/tcpip.h"
+#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty()
+static u8_t pbuf_free_ooseq_queued;
+/**
+ * Attempt to reclaim some memory from queued out-of-sequence TCP segments
+ * if we run out of pool pbufs. It's better to give priority to new packets
+ * if we're running out.
+ *
+ * This must be done in the correct thread context therefore this function
+ * can only be used with NO_SYS=0 and through tcpip_callback.
+ */
+static void
+pbuf_free_ooseq(void* arg)
+{
+ struct tcp_pcb* pcb;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ LWIP_UNUSED_ARG(arg);
+
+ SYS_ARCH_PROTECT(old_level);
+ pbuf_free_ooseq_queued = 0;
+ SYS_ARCH_UNPROTECT(old_level);
+
+ for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
+ if (NULL != pcb->ooseq) {
+ /** Free the ooseq pbufs of one PCB only */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+ return;
+ }
+ }
+}
+
+/** Queue a call to pbuf_free_ooseq if not already queued. */
+static void
+pbuf_pool_is_empty(void)
+{
+ u8_t queued;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ SYS_ARCH_PROTECT(old_level);
+ queued = pbuf_free_ooseq_queued;
+ pbuf_free_ooseq_queued = 1;
+ SYS_ARCH_UNPROTECT(old_level);
+
+ if(!queued) {
+ /* queue a call to pbuf_free_ooseq if not already queued */
+ if(tcpip_callback_with_block(pbuf_free_ooseq, NULL, 0) != ERR_OK) {
+ SYS_ARCH_PROTECT(old_level);
+ pbuf_free_ooseq_queued = 0;
+ SYS_ARCH_UNPROTECT(old_level);
+ }
+ }
+}
+#endif /* PBUF_POOL_FREE_OOSEQ */
+#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */
+
+/**
+ * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
+ *
+ * The actual memory allocated for the pbuf is determined by the
+ * layer at which the pbuf is allocated and the requested size
+ * (from the size parameter).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_RAM: buffer memory for pbuf is allocated as one large
+ * chunk. This includes protocol headers as well.
+ * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
+ * protocol headers. Additional headers must be prepended
+ * by allocating another pbuf and chain in to the front of
+ * the ROM pbuf. It is assumed that the memory used is really
+ * similar to ROM in that it is immutable and will not be
+ * changed. Memory which is dynamic should generally not
+ * be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
+ * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
+ * protocol headers. It is assumed that the pbuf is only
+ * being used in a single thread. If the pbuf gets queued,
+ * then pbuf_take should be called to copy the buffer.
+ * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
+ * the pbuf pool that is allocated during pbuf_init().
+ *
+ * @return the allocated pbuf. If multiple pbufs where allocated, this
+ * is the first pbuf of a pbuf chain.
+ */
+struct pbuf *
+pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
+{
+ struct pbuf *p, *q, *r;
+ u16_t offset;
+ s32_t rem_len; /* remaining length */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
+
+ /* determine header offset */
+ offset = 0;
+ switch (layer) {
+ case PBUF_TRANSPORT:
+ /* add room for transport (often TCP) layer header */
+ offset += PBUF_TRANSPORT_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_IP:
+ /* add room for IP layer header */
+ offset += PBUF_IP_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_LINK:
+ /* add room for link layer header */
+ offset += PBUF_LINK_HLEN;
+ break;
+ case PBUF_RAW:
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
+ return NULL;
+ }
+
+ switch (type) {
+ case PBUF_POOL:
+ /* allocate head of pbuf chain into p */
+ p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p));
+ if (p == NULL) {
+ PBUF_POOL_IS_EMPTY();
+ return NULL;
+ }
+ p->type = type;
+ p->next = NULL;
+
+ /* make the payload pointer point 'offset' bytes into pbuf data memory */
+ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
+ LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ /* the total length of the pbuf chain is the requested size */
+ p->tot_len = length;
+ /* set the length of the first pbuf in the chain */
+ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));
+ LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+ ((u8_t*)p->payload + p->len <=
+ (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+ LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
+ (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
+ /* set reference count (needed here in case we fail) */
+ p->ref = 1;
+
+ /* now allocate the tail of the pbuf chain */
+
+ /* remember first pbuf for linkage in next iteration */
+ r = p;
+ /* remaining length to be allocated */
+ rem_len = length - p->len;
+ /* any remaining pbufs to be allocated? */
+ while (rem_len > 0) {
+ q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+ if (q == NULL) {
+ PBUF_POOL_IS_EMPTY();
+ /* free chain so far allocated */
+ pbuf_free(p);
+ /* bail out unsuccesfully */
+ return NULL;
+ }
+ q->type = type;
+ q->flags = 0;
+ q->next = NULL;
+ /* make previous pbuf point to this pbuf */
+ r->next = q;
+ /* set total length of this pbuf and next in chain */
+ LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff);
+ q->tot_len = (u16_t)rem_len;
+ /* this pbuf length is pool size, unless smaller sized tail */
+ q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
+ q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
+ LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
+ ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
+ LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+ ((u8_t*)p->payload + p->len <=
+ (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+ q->ref = 1;
+ /* calculate remaining length to be allocated */
+ rem_len -= q->len;
+ /* remember this pbuf for linkage in next iteration */
+ r = q;
+ }
+ /* end of chain */
+ /*r->next = NULL;*/
+
+ break;
+ case PBUF_RAM:
+ /* If pbuf is to be allocated in RAM, allocate memory for it. */
+ p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
+ if (p == NULL) {
+ return NULL;
+ }
+ /* Set up internal structure of the pbuf. */
+ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
+ p->len = p->tot_len = length;
+ p->next = NULL;
+ p->type = type;
+
+ LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ break;
+ /* pbuf references existing (non-volatile static constant) ROM payload? */
+ case PBUF_ROM:
+ /* pbuf references existing (externally allocated) RAM payload? */
+ case PBUF_REF:
+ /* only allocate memory for the pbuf structure */
+ p = (struct pbuf *)memp_malloc(MEMP_PBUF);
+ if (p == NULL) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n",
+ (type == PBUF_ROM) ? "ROM" : "REF"));
+ return NULL;
+ }
+ /* caller must set this field properly, afterwards */
+ p->payload = NULL;
+ p->len = p->tot_len = length;
+ p->next = NULL;
+ p->type = type;
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
+ return NULL;
+ }
+ /* set reference count */
+ p->ref = 1;
+ /* set flags */
+ p->flags = 0;
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
+ return p;
+}
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/** Initialize a custom pbuf (already allocated).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type type of the pbuf (only used to treat the pbuf accordingly, as
+ * this function allocates no memory)
+ * @param p pointer to the custom pbuf to initialize (already allocated)
+ * @param payload_mem pointer to the buffer that is used for payload and headers,
+ * must be at least big enough to hold 'length' plus the header size,
+ * may be NULL if set later
+ * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least
+ * big enough to hold 'length' plus the header size
+ */
+struct pbuf*
+pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
+ void *payload_mem, u16_t payload_mem_len)
+{
+ u16_t offset;
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
+
+ /* determine header offset */
+ offset = 0;
+ switch (l) {
+ case PBUF_TRANSPORT:
+ /* add room for transport (often TCP) layer header */
+ offset += PBUF_TRANSPORT_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_IP:
+ /* add room for IP layer header */
+ offset += PBUF_IP_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_LINK:
+ /* add room for link layer header */
+ offset += PBUF_LINK_HLEN;
+ break;
+ case PBUF_RAW:
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
+ return NULL;
+ }
+
+ if (LWIP_MEM_ALIGN_SIZE(offset) + length < payload_mem_len) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
+ return NULL;
+ }
+
+ p->pbuf.next = NULL;
+ if (payload_mem != NULL) {
+ p->pbuf.payload = LWIP_MEM_ALIGN((void *)((u8_t *)payload_mem + offset));
+ } else {
+ p->pbuf.payload = NULL;
+ }
+ p->pbuf.flags = PBUF_FLAG_IS_CUSTOM;
+ p->pbuf.len = p->pbuf.tot_len = length;
+ p->pbuf.type = type;
+ p->pbuf.ref = 1;
+ return &p->pbuf;
+}
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/**
+ * Shrink a pbuf chain to a desired length.
+ *
+ * @param p pbuf to shrink.
+ * @param new_len desired new length of pbuf chain
+ *
+ * Depending on the desired length, the first few pbufs in a chain might
+ * be skipped and left unchanged. The new last pbuf in the chain will be
+ * resized, and any remaining pbufs will be freed.
+ *
+ * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted.
+ * @note May not be called on a packet queue.
+ *
+ * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
+ */
+void
+pbuf_realloc(struct pbuf *p, u16_t new_len)
+{
+ struct pbuf *q;
+ u16_t rem_len; /* remaining length */
+ s32_t grow;
+
+ LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
+ LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
+ p->type == PBUF_ROM ||
+ p->type == PBUF_RAM ||
+ p->type == PBUF_REF);
+
+ /* desired length larger than current length? */
+ if (new_len >= p->tot_len) {
+ /* enlarging not yet supported */
+ return;
+ }
+
+ /* the pbuf chain grows by (new_len - p->tot_len) bytes
+ * (which may be negative in case of shrinking) */
+ grow = new_len - p->tot_len;
+
+ /* first, step over any pbufs that should remain in the chain */
+ rem_len = new_len;
+ q = p;
+ /* should this pbuf be kept? */
+ while (rem_len > q->len) {
+ /* decrease remaining length by pbuf length */
+ rem_len -= q->len;
+ /* decrease total length indicator */
+ LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
+ q->tot_len += (u16_t)grow;
+ /* proceed to next pbuf in chain */
+ q = q->next;
+ LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
+ }
+ /* we have now reached the new last pbuf (in q) */
+ /* rem_len == desired length for pbuf q */
+
+ /* shrink allocated memory for PBUF_RAM */
+ /* (other types merely adjust their length fields */
+ if ((q->type == PBUF_RAM) && (rem_len != q->len)) {
+ /* reallocate and adjust the length of the pbuf that will be split */
+ q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
+ LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
+ }
+ /* adjust length fields for new last pbuf */
+ q->len = rem_len;
+ q->tot_len = q->len;
+
+ /* any remaining pbufs in chain? */
+ if (q->next != NULL) {
+ /* free remaining pbufs in chain */
+ pbuf_free(q->next);
+ }
+ /* q is last packet in chain */
+ q->next = NULL;
+
+}
+
+/**
+ * Adjusts the payload pointer to hide or reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * (dis)appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ * increases the size of the pbuf. New space is on the front.
+ * (Using a negative value decreases the header size.)
+ * If hdr_size_inc is 0, this function does nothing and returns succesful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_header(struct pbuf *p, s16_t header_size_increment)
+{
+ u16_t type;
+ void *payload;
+ u16_t increment_magnitude;
+
+ LWIP_ASSERT("p != NULL", p != NULL);
+ if ((header_size_increment == 0) || (p == NULL)) {
+ return 0;
+ }
+
+ if (header_size_increment < 0){
+ increment_magnitude = -header_size_increment;
+ /* Check that we aren't going to move off the end of the pbuf */
+ LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
+ } else {
+ increment_magnitude = header_size_increment;
+#if 0
+ /* Can't assert these as some callers speculatively call
+ pbuf_header() to see if it's OK. Will return 1 below instead. */
+ /* Check that we've got the correct type of pbuf to work with */
+ LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL",
+ p->type == PBUF_RAM || p->type == PBUF_POOL);
+ /* Check that we aren't going to move off the beginning of the pbuf */
+ LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF",
+ (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF);
+#endif
+ }
+
+ type = p->type;
+ /* remember current payload pointer */
+ payload = p->payload;
+
+ /* pbuf types containing payloads? */
+ if (type == PBUF_RAM || type == PBUF_POOL) {
+ /* set new payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ /* boundary check fails? */
+ if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_header: failed as %p < %p (not enough space for new header size)\n",
+ (void *)p->payload, (void *)(p + 1)));
+ /* restore old payload pointer */
+ p->payload = payload;
+ /* bail out unsuccesfully */
+ return 1;
+ }
+ /* pbuf types refering to external payloads? */
+ } else if (type == PBUF_REF || type == PBUF_ROM) {
+ /* hide a header in the payload? */
+ if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {
+ /* increase payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ } else {
+ /* cannot expand payload to front (yet!)
+ * bail out unsuccesfully */
+ return 1;
+ }
+ } else {
+ /* Unknown type */
+ LWIP_ASSERT("bad pbuf type", 0);
+ return 1;
+ }
+ /* modify pbuf length fields */
+ p->len += header_size_increment;
+ p->tot_len += header_size_increment;
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n",
+ (void *)payload, (void *)p->payload, header_size_increment));
+
+ return 0;
+}
+
+/**
+ * Dereference a pbuf chain or queue and deallocate any no-longer-used
+ * pbufs at the head of this chain or queue.
+ *
+ * Decrements the pbuf reference count. If it reaches zero, the pbuf is
+ * deallocated.
+ *
+ * For a pbuf chain, this is repeated for each pbuf in the chain,
+ * up to the first pbuf which has a non-zero reference count after
+ * decrementing. So, when all reference counts are one, the whole
+ * chain is free'd.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ *
+ * @return the number of pbufs that were de-allocated
+ * from the head of the chain.
+ *
+ * @note MUST NOT be called on a packet queue (Not verified to work yet).
+ * @note the reference counter of a pbuf equals the number of pointers
+ * that refer to the pbuf (or into the pbuf).
+ *
+ * @internal examples:
+ *
+ * Assuming existing chains a->b->c with the following reference
+ * counts, calling pbuf_free(a) results in:
+ *
+ * 1->2->3 becomes ...1->3
+ * 3->3->3 becomes 2->3->3
+ * 1->1->2 becomes ......1
+ * 2->1->1 becomes 1->1->1
+ * 1->1->1 becomes .......
+ *
+ */
+u8_t
+pbuf_free(struct pbuf *p)
+{
+ u16_t type;
+ struct pbuf *q;
+ u8_t count;
+
+ if (p == NULL) {
+ LWIP_ASSERT("p != NULL", p != NULL);
+ /* if assertions are disabled, proceed with debug output */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_free(p == NULL) was called.\n"));
+ return 0;
+ }
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));
+
+ PERF_START;
+
+ LWIP_ASSERT("pbuf_free: sane type",
+ p->type == PBUF_RAM || p->type == PBUF_ROM ||
+ p->type == PBUF_REF || p->type == PBUF_POOL);
+
+ count = 0;
+ /* de-allocate all consecutive pbufs from the head of the chain that
+ * obtain a zero reference count after decrementing*/
+ while (p != NULL) {
+ u16_t ref;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ /* Since decrementing ref cannot be guaranteed to be a single machine operation
+ * we must protect it. We put the new ref into a local variable to prevent
+ * further protection. */
+ SYS_ARCH_PROTECT(old_level);
+ /* all pbufs in a chain are referenced at least once */
+ LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
+ /* decrease reference count (number of pointers to pbuf) */
+ ref = --(p->ref);
+ SYS_ARCH_UNPROTECT(old_level);
+ /* this pbuf is no longer referenced to? */
+ if (ref == 0) {
+ /* remember next pbuf in chain for next iteration */
+ q = p->next;
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
+ type = p->type;
+#if LWIP_SUPPORT_CUSTOM_PBUF
+ /* is this a custom pbuf? */
+ if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
+ struct pbuf_custom *pc = (struct pbuf_custom*)p;
+ LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);
+ pc->custom_free_function(p);
+ } else
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+ {
+ /* is this a pbuf from the pool? */
+ if (type == PBUF_POOL) {
+ memp_free(MEMP_PBUF_POOL, p);
+ /* is this a ROM or RAM referencing pbuf? */
+ } else if (type == PBUF_ROM || type == PBUF_REF) {
+ memp_free(MEMP_PBUF, p);
+ /* type == PBUF_RAM */
+ } else {
+ mem_free(p);
+ }
+ }
+ count++;
+ /* proceed to next pbuf */
+ p = q;
+ /* p->ref > 0, this pbuf is still referenced to */
+ /* (and so the remaining pbufs in chain as well) */
+ } else {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref));
+ /* stop walking through the chain */
+ p = NULL;
+ }
+ }
+ PERF_STOP("pbuf_free");
+ /* return number of de-allocated pbufs */
+ return count;
+}
+
+/**
+ * Count number of pbufs in a chain
+ *
+ * @param p first pbuf of chain
+ * @return the number of pbufs in a chain
+ */
+
+u8_t
+pbuf_clen(struct pbuf *p)
+{
+ u8_t len;
+
+ len = 0;
+ while (p != NULL) {
+ ++len;
+ p = p->next;
+ }
+ return len;
+}
+
+/**
+ * Increment the reference count of the pbuf.
+ *
+ * @param p pbuf to increase reference counter of
+ *
+ */
+void
+pbuf_ref(struct pbuf *p)
+{
+ SYS_ARCH_DECL_PROTECT(old_level);
+ /* pbuf given? */
+ if (p != NULL) {
+ SYS_ARCH_PROTECT(old_level);
+ ++(p->ref);
+ SYS_ARCH_UNPROTECT(old_level);
+ }
+}
+
+/**
+ * Concatenate two pbufs (each may be a pbuf chain) and take over
+ * the caller's reference of the tail pbuf.
+ *
+ * @note The caller MAY NOT reference the tail pbuf afterwards.
+ * Use pbuf_chain() for that purpose.
+ *
+ * @see pbuf_chain()
+ */
+
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+ struct pbuf *p;
+
+ LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
+ ((h != NULL) && (t != NULL)), return;);
+
+ /* proceed to last pbuf of chain */
+ for (p = h; p->next != NULL; p = p->next) {
+ /* add total length of second chain to all totals of first chain */
+ p->tot_len += t->tot_len;
+ }
+ /* { p is last pbuf of first h chain, p->next == NULL } */
+ LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len);
+ LWIP_ASSERT("p->next == NULL", p->next == NULL);
+ /* add total length of second chain to last pbuf total of first chain */
+ p->tot_len += t->tot_len;
+ /* chain last pbuf of head (p) with first of tail (t) */
+ p->next = t;
+ /* p->next now references t, but the caller will drop its reference to t,
+ * so netto there is no change to the reference count of t.
+ */
+}
+
+/**
+ * Chain two pbufs (or pbuf chains) together.
+ *
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ *
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+ pbuf_cat(h, t);
+ /* t is now referenced by h */
+ pbuf_ref(t);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
+}
+
+/**
+ * Dechains the first pbuf from its succeeding pbufs in the chain.
+ *
+ * Makes p->tot_len field equal to p->len.
+ * @param p pbuf to dechain
+ * @return remainder of the pbuf chain, or NULL if it was de-allocated.
+ * @note May not be called on a packet queue.
+ */
+struct pbuf *
+pbuf_dechain(struct pbuf *p)
+{
+ struct pbuf *q;
+ u8_t tail_gone = 1;
+ /* tail */
+ q = p->next;
+ /* pbuf has successor in chain? */
+ if (q != NULL) {
+ /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+ LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
+ /* enforce invariant if assertion is disabled */
+ q->tot_len = p->tot_len - p->len;
+ /* decouple pbuf from remainder */
+ p->next = NULL;
+ /* total length of pbuf p is its own length only */
+ p->tot_len = p->len;
+ /* q is no longer referenced by p, free it */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
+ tail_gone = pbuf_free(q);
+ if (tail_gone > 0) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
+ ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
+ }
+ /* return remaining tail or NULL if deallocated */
+ }
+ /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+ LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
+ return ((tail_gone > 0) ? NULL : q);
+}
+
+/**
+ *
+ * Create PBUF_RAM copies of pbufs.
+ *
+ * Used to queue packets on behalf of the lwIP stack, such as
+ * ARP based queueing.
+ *
+ * @note You MUST explicitly use p = pbuf_take(p);
+ *
+ * @note Only one packet is copied, no packet queue!
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ *
+ * @return ERR_OK if pbuf was copied
+ * ERR_ARG if one of the pbufs is NULL or p_to is not big
+ * enough to hold p_from
+ */
+err_t
+pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
+{
+ u16_t offset_to=0, offset_from=0, len;
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
+ (void*)p_to, (void*)p_from));
+
+ /* is the target big enough to hold the source? */
+ LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
+ (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
+
+ /* iterate through pbuf chain */
+ do
+ {
+ LWIP_ASSERT("p_to != NULL", p_to != NULL);
+ /* copy one part of the original chain */
+ if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
+ /* complete current p_from fits into current p_to */
+ len = p_from->len - offset_from;
+ } else {
+ /* current p_from does not fit into current p_to */
+ len = p_to->len - offset_to;
+ }
+ MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
+ offset_to += len;
+ offset_from += len;
+ LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
+ if (offset_to == p_to->len) {
+ /* on to next p_to (if any) */
+ offset_to = 0;
+ p_to = p_to->next;
+ }
+ LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
+ if (offset_from >= p_from->len) {
+ /* on to next p_from (if any) */
+ offset_from = 0;
+ p_from = p_from->next;
+ }
+
+ if((p_from != NULL) && (p_from->len == p_from->tot_len)) {
+ /* don't copy more than one packet! */
+ LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+ (p_from->next == NULL), return ERR_VAL;);
+ }
+ if((p_to != NULL) && (p_to->len == p_to->tot_len)) {
+ /* don't copy more than one packet! */
+ LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+ (p_to->next == NULL), return ERR_VAL;);
+ }
+ } while (p_from);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
+ return ERR_OK;
+}
+
+/**
+ * Copy (part of) the contents of a packet buffer
+ * to an application supplied buffer.
+ *
+ * @param buf the pbuf from which to copy data
+ * @param dataptr the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+u16_t
+pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
+{
+ struct pbuf *p;
+ u16_t left;
+ u16_t buf_copy_len;
+ u16_t copied_total = 0;
+
+ LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;);
+ LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;);
+
+ left = 0;
+
+ if((buf == NULL) || (dataptr == NULL)) {
+ return 0;
+ }
+
+ /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+ for(p = buf; len != 0 && p != NULL; p = p->next) {
+ if ((offset != 0) && (offset >= p->len)) {
+ /* don't copy from this buffer -> on to the next */
+ offset -= p->len;
+ } else {
+ /* copy from this buffer. maybe only partially. */
+ buf_copy_len = p->len - offset;
+ if (buf_copy_len > len)
+ buf_copy_len = len;
+ /* copy the necessary parts of the buffer */
+ MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
+ copied_total += buf_copy_len;
+ left += buf_copy_len;
+ len -= buf_copy_len;
+ offset = 0;
+ }
+ }
+ return copied_total;
+}
+
+/**
+ * Copy application supplied data into a pbuf.
+ * This function can only be used to copy the equivalent of buf->tot_len data.
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
+{
+ struct pbuf *p;
+ u16_t buf_copy_len;
+ u16_t total_copy_len = len;
+ u16_t copied_total = 0;
+
+ LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;);
+ LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;);
+
+ if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
+ return ERR_ARG;
+ }
+
+ /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+ for(p = buf; total_copy_len != 0; p = p->next) {
+ LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
+ buf_copy_len = total_copy_len;
+ if (buf_copy_len > p->len) {
+ /* this pbuf cannot hold all remaining data */
+ buf_copy_len = p->len;
+ }
+ /* copy the necessary parts of the buffer */
+ MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len);
+ total_copy_len -= buf_copy_len;
+ copied_total += buf_copy_len;
+ }
+ LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
+ return ERR_OK;
+}
+
+/**
+ * Creates a single pbuf out of a queue of pbufs.
+ *
+ * @remark: Either the source pbuf 'p' is freed by this function or the original
+ * pbuf 'p' is returned, therefore the caller has to check the result!
+ *
+ * @param p the source pbuf
+ * @param layer pbuf_layer of the new pbuf
+ *
+ * @return a new, single pbuf (p->next is NULL)
+ * or the old pbuf if allocation fails
+ */
+struct pbuf*
+pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
+{
+ struct pbuf *q;
+ err_t err;
+ if (p->next == NULL) {
+ return p;
+ }
+ q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
+ if (q == NULL) {
+ /* @todo: what do we do now? */
+ return p;
+ }
+ err = pbuf_copy(q, p);
+ LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
+ pbuf_free(p);
+ return q;
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/**
+ * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
+ * the checksum while copying
+ *
+ * @param p the pbuf to copy data into
+ * @param start_offset offset of p->payload where to copy the data to
+ * @param dataptr data to copy into the pbuf
+ * @param len length of data to copy into the pbuf
+ * @param chksum pointer to the checksum which is updated
+ * @return ERR_OK if successful, another error if the data does not fit
+ * within the (first) pbuf (no pbuf queues!)
+ */
+err_t
+pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+ u16_t len, u16_t *chksum)
+{
+ u32_t acc;
+ u16_t copy_chksum;
+ char *dst_ptr;
+ LWIP_ASSERT("p != NULL", p != NULL);
+ LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
+ LWIP_ASSERT("chksum != NULL", chksum != NULL);
+ LWIP_ASSERT("len != 0", len != 0);
+
+ if ((start_offset >= p->len) || (start_offset + len > p->len)) {
+ return ERR_ARG;
+ }
+
+ dst_ptr = ((char*)p->payload) + start_offset;
+ copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
+ if ((start_offset & 1) != 0) {
+ copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
+ }
+ acc = *chksum;
+ acc += copy_chksum;
+ *chksum = FOLD_U32T(acc);
+ return ERR_OK;
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+ /** Get one byte from the specified position in a pbuf
+ * WARNING: returns zero for offset >= p->tot_len
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
+ */
+u8_t
+pbuf_get_at(struct pbuf* p, u16_t offset)
+{
+ u16_t copy_from = offset;
+ struct pbuf* q = p;
+
+ /* get the correct pbuf */
+ while ((q != NULL) && (q->len <= copy_from)) {
+ copy_from -= q->len;
+ q = q->next;
+ }
+ /* return requested data if pbuf is OK */
+ if ((q != NULL) && (q->len > copy_from)) {
+ return ((u8_t*)q->payload)[copy_from];
+ }
+ return 0;
+}
+
+/** Compare pbuf contents at specified offset with memory s2, both of length n
+ *
+ * @param p pbuf to compare
+ * @param offset offset into p at wich to start comparing
+ * @param s2 buffer to compare
+ * @param n length of buffer to compare
+ * @return zero if equal, nonzero otherwise
+ * (0xffff if p is too short, diffoffset+1 otherwise)
+ */
+u16_t
+pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
+{
+ u16_t start = offset;
+ struct pbuf* q = p;
+
+ /* get the correct pbuf */
+ while ((q != NULL) && (q->len <= start)) {
+ start -= q->len;
+ q = q->next;
+ }
+ /* return requested data if pbuf is OK */
+ if ((q != NULL) && (q->len > start)) {
+ u16_t i;
+ for(i = 0; i < n; i++) {
+ u8_t a = pbuf_get_at(q, start + i);
+ u8_t b = ((u8_t*)s2)[i];
+ if (a != b) {
+ return i+1;
+ }
+ }
+ return 0;
+ }
+ return 0xffff;
+}
+
+/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
+ * start_offset.
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ * return value 'not found'
+ * @param mem search for the contents of this buffer
+ * @param mem_len length of 'mem'
+ * @param start_offset offset into p at which to start searching
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
+{
+ u16_t i;
+ u16_t max = p->tot_len - mem_len;
+ if (p->tot_len >= mem_len + start_offset) {
+ for(i = start_offset; i <= max; ) {
+ u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
+ if (plus == 0) {
+ return i;
+ } else {
+ i += plus;
+ }
+ }
+ }
+ return 0xFFFF;
+}
+
+/** Find occurrence of substr with length substr_len in pbuf p, start at offset
+ * start_offset
+ * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
+ * the pbuf/source string!
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ * return value 'not found'
+ * @param substr string to search for in p, maximum length is 0xFFFE
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_strstr(struct pbuf* p, const char* substr)
+{
+ size_t substr_len;
+ if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
+ return 0xFFFF;
+ }
+ substr_len = strlen(substr);
+ if (substr_len >= 0xFFFF) {
+ return 0xFFFF;
+ }
+ return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/raw.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,352 @@
+/**
+ * @file
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/raw.h"
+#include "lwip/stats.h"
+#include "arch/perf.h"
+
+#include <string.h>
+
+/** The list of RAW PCBs */
+static struct raw_pcb *raw_pcbs;
+
+/**
+ * Determine if in incoming IP packet is covered by a RAW PCB
+ * and if so, pass it to a user-provided receive callback function.
+ *
+ * Given an incoming IP datagram (as a chain of pbufs) this function
+ * finds a corresponding RAW PCB and calls the corresponding receive
+ * callback function.
+ *
+ * @param p pbuf to be demultiplexed to a RAW PCB.
+ * @param inp network interface on which the datagram was received.
+ * @return - 1 if the packet has been eaten by a RAW PCB receive
+ * callback function. The caller MAY NOT not reference the
+ * packet any longer, and MAY NOT call pbuf_free().
+ * @return - 0 if packet is not eaten (pbuf is still referenced by the
+ * caller).
+ *
+ */
+u8_t
+raw_input(struct pbuf *p, struct netif *inp)
+{
+ struct raw_pcb *pcb, *prev;
+ struct ip_hdr *iphdr;
+ s16_t proto;
+ u8_t eaten = 0;
+
+ LWIP_UNUSED_ARG(inp);
+
+ iphdr = (struct ip_hdr *)p->payload;
+ proto = IPH_PROTO(iphdr);
+
+ prev = NULL;
+ pcb = raw_pcbs;
+ /* loop through all raw pcbs until the packet is eaten by one */
+ /* this allows multiple pcbs to match against the packet by design */
+ while ((eaten == 0) && (pcb != NULL)) {
+ if ((pcb->protocol == proto) &&
+ (ip_addr_isany(&pcb->local_ip) ||
+ ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest))) {
+#if IP_SOF_BROADCAST_RECV
+ /* broadcast filter? */
+ if ((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(¤t_iphdr_dest, inp))
+#endif /* IP_SOF_BROADCAST_RECV */
+ {
+ /* receive callback function available? */
+ if (pcb->recv != NULL) {
+ /* the receive callback function did not eat the packet? */
+ if (pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr()) != 0) {
+ /* receive function ate the packet */
+ p = NULL;
+ eaten = 1;
+ if (prev != NULL) {
+ /* move the pcb to the front of raw_pcbs so that is
+ found faster next time */
+ prev->next = pcb->next;
+ pcb->next = raw_pcbs;
+ raw_pcbs = pcb;
+ }
+ }
+ }
+ /* no receive callback function was set for this raw PCB */
+ }
+ /* drop the packet */
+ }
+ prev = pcb;
+ pcb = pcb->next;
+ }
+ return eaten;
+}
+
+/**
+ * Bind a RAW PCB.
+ *
+ * @param pcb RAW PCB to be bound with a local address ipaddr.
+ * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to
+ * bind to all local interfaces.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_USE. The specified IP address is already bound to by
+ * another RAW PCB.
+ *
+ * @see raw_disconnect()
+ */
+err_t
+raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr)
+{
+ ip_addr_set(&pcb->local_ip, ipaddr);
+ return ERR_OK;
+}
+
+/**
+ * Connect an RAW PCB. This function is required by upper layers
+ * of lwip. Using the raw api you could use raw_sendto() instead
+ *
+ * This will associate the RAW PCB with the remote address.
+ *
+ * @param pcb RAW PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ *
+ * @return lwIP error code
+ *
+ * @see raw_disconnect() and raw_sendto()
+ */
+err_t
+raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr)
+{
+ ip_addr_set(&pcb->remote_ip, ipaddr);
+ return ERR_OK;
+}
+
+
+/**
+ * Set the callback function for received packets that match the
+ * raw PCB's protocol and binding.
+ *
+ * The callback function MUST either
+ * - eat the packet by calling pbuf_free() and returning non-zero. The
+ * packet will not be passed to other raw PCBs or other protocol layers.
+ * - not free the packet, and return zero. The packet will be matched
+ * against further PCBs and/or forwarded to another protocol layers.
+ *
+ * @return non-zero if the packet was free()d, zero if the packet remains
+ * available for others.
+ */
+void
+raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
+{
+ /* remember recv() callback and user data */
+ pcb->recv = recv;
+ pcb->recv_arg = recv_arg;
+}
+
+/**
+ * Send the raw IP packet to the given address. Note that actually you cannot
+ * modify the IP headers (this is inconsistent with the receive callback where
+ * you actually get the IP headers), you can only specify the IP payload here.
+ * It requires some more changes in lwIP. (there will be a raw_send() function
+ * then.)
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ * @param ipaddr the destination address of the IP packet
+ *
+ */
+err_t
+raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
+{
+ err_t err;
+ struct netif *netif;
+ ip_addr_t *src_ip;
+ struct pbuf *q; /* q will be sent down the stack */
+
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
+
+ /* not enough space to add an IP header to first pbuf in given p chain? */
+ if (pbuf_header(p, IP_HLEN)) {
+ /* allocate header in new pbuf */
+ q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
+ /* new header pbuf could not be allocated? */
+ if (q == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n"));
+ return ERR_MEM;
+ }
+ /* chain header q in front of given pbuf p */
+ pbuf_chain(q, p);
+ /* { first pbuf q points to header pbuf } */
+ LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+ } else {
+ /* first pbuf q equals given pbuf */
+ q = p;
+ if(pbuf_header(q, -IP_HLEN)) {
+ LWIP_ASSERT("Can't restore header we just removed!", 0);
+ return ERR_MEM;
+ }
+ }
+
+ if ((netif = ip_route(ipaddr)) == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+ /* free any temporary header pbuf allocated by pbuf_header() */
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_RTE;
+ }
+
+#if IP_SOF_BROADCAST
+ /* broadcast filter? */
+ if (((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(ipaddr, netif)) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+ /* free any temporary header pbuf allocated by pbuf_header() */
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_VAL;
+ }
+#endif /* IP_SOF_BROADCAST */
+
+ if (ip_addr_isany(&pcb->local_ip)) {
+ /* use outgoing network interface IP address as source address */
+ src_ip = &(netif->ip_addr);
+ } else {
+ /* use RAW PCB local IP address as source address */
+ src_ip = &(pcb->local_ip);
+ }
+
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = &(pcb->addr_hint);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = NULL;
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+ /* did we chain a header earlier? */
+ if (q != p) {
+ /* free the header */
+ pbuf_free(q);
+ }
+ return err;
+}
+
+/**
+ * Send the raw IP packet to the address given by raw_connect()
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ *
+ */
+err_t
+raw_send(struct raw_pcb *pcb, struct pbuf *p)
+{
+ return raw_sendto(pcb, p, &pcb->remote_ip);
+}
+
+/**
+ * Remove an RAW PCB.
+ *
+ * @param pcb RAW PCB to be removed. The PCB is removed from the list of
+ * RAW PCB's and the data structure is freed from memory.
+ *
+ * @see raw_new()
+ */
+void
+raw_remove(struct raw_pcb *pcb)
+{
+ struct raw_pcb *pcb2;
+ /* pcb to be removed is first in list? */
+ if (raw_pcbs == pcb) {
+ /* make list start at 2nd pcb */
+ raw_pcbs = raw_pcbs->next;
+ /* pcb not 1st in list */
+ } else {
+ for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+ /* find pcb in raw_pcbs list */
+ if (pcb2->next != NULL && pcb2->next == pcb) {
+ /* remove pcb from list */
+ pcb2->next = pcb->next;
+ }
+ }
+ }
+ memp_free(MEMP_RAW_PCB, pcb);
+}
+
+/**
+ * Create a RAW PCB.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb *
+raw_new(u8_t proto)
+{
+ struct raw_pcb *pcb;
+
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n"));
+
+ pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB);
+ /* could allocate RAW PCB? */
+ if (pcb != NULL) {
+ /* initialize PCB to all zeroes */
+ memset(pcb, 0, sizeof(struct raw_pcb));
+ pcb->protocol = proto;
+ pcb->ttl = RAW_TTL;
+ pcb->next = raw_pcbs;
+ raw_pcbs = pcb;
+ }
+ return pcb;
+}
+
+#endif /* LWIP_RAW */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/snmp/asn1_dec.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,657 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) decoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_asn1.h"
+
+/**
+ * Retrieves type field from incoming pbuf chain.
+ *
+ * @param p points to a pbuf holding an ASN1 coded type field
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field
+ * @param type return ASN1 type
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ *type = *msg_ptr;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes length field from incoming pbuf chain into host length.
+ *
+ * @param p points to a pbuf holding an ASN1 coded length
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded length
+ * @param octets_used returns number of octets used by the length code
+ * @param length return host order length, upto 64k
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (*msg_ptr < 0x80)
+ {
+ /* primitive definite length format */
+ *octets_used = 1;
+ *length = *msg_ptr;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x80)
+ {
+ /* constructed indefinite length format, termination with two zero octets */
+ u8_t zeros;
+ u8_t i;
+
+ *length = 0;
+ zeros = 0;
+ while (zeros != 2)
+ {
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ (*length) += 1;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (*msg_ptr == 0)
+ {
+ zeros++;
+ if (zeros == 2)
+ {
+ /* stop while (i > 0) */
+ i = 0;
+ }
+ }
+ else
+ {
+ zeros = 0;
+ }
+ }
+ }
+ *octets_used = 1;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x81)
+ {
+ /* constructed definite length format, one octet */
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ *length = *msg_ptr;
+ *octets_used = 2;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x82)
+ {
+ u8_t i;
+
+ /* constructed definite length format, two octets */
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (i == 0)
+ {
+ /* least significant length octet */
+ *length |= *msg_ptr;
+ }
+ else
+ {
+ /* most significant length octet */
+ *length = (*msg_ptr) << 8;
+ }
+ }
+ *octets_used = 3;
+ return ERR_OK;
+ }
+ else
+ {
+ /* constructed definite length format 3..127 octets, this is too big (>64k) */
+ /** @todo: do we need to accept inefficient codings with many leading zero's? */
+ *octets_used = 1 + ((*msg_ptr) & 0x7f);
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes positive integer (counter, gauge, timeticks) into u32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded integer
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+err_t
+snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if ((len > 0) && (len < 6))
+ {
+ /* start from zero */
+ *value = 0;
+ if (*msg_ptr & 0x80)
+ {
+ /* negative, expecting zero sign bit! */
+ return ERR_ARG;
+ }
+ else
+ {
+ /* positive */
+ if ((len > 1) && (*msg_ptr == 0))
+ {
+ /* skip leading "sign byte" octet 0x00 */
+ len--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ }
+ /* OR octets with value */
+ while (len > 1)
+ {
+ len--;
+ *value |= *msg_ptr;
+ *value <<= 8;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ *value |= *msg_ptr;
+ return ERR_OK;
+ }
+ else
+ {
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes integer into s32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded integer
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed!
+ */
+err_t
+snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u8_t *lsb_ptr = (u8_t*)value;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
+#endif
+ u8_t sign;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if ((len > 0) && (len < 5))
+ {
+ if (*msg_ptr & 0x80)
+ {
+ /* negative, start from -1 */
+ *value = -1;
+ sign = 1;
+ }
+ else
+ {
+ /* positive, start from 0 */
+ *value = 0;
+ sign = 0;
+ }
+ /* OR/AND octets with value */
+ while (len > 1)
+ {
+ len--;
+ if (sign)
+ {
+ *lsb_ptr &= *msg_ptr;
+ *value <<= 8;
+ *lsb_ptr |= 255;
+ }
+ else
+ {
+ *lsb_ptr |= *msg_ptr;
+ *value <<= 8;
+ }
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (sign)
+ {
+ *lsb_ptr &= *msg_ptr;
+ }
+ else
+ {
+ *lsb_ptr |= *msg_ptr;
+ }
+ return ERR_OK;
+ }
+ else
+ {
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes object identifier from incoming message into array of s32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded object identifier
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier
+ * @param len length of the coded object identifier
+ * @param oid return object identifier struct
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+ s32_t *oid_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ oid->len = 0;
+ oid_ptr = &oid->id[0];
+ if (len > 0)
+ {
+ /* first compressed octet */
+ if (*msg_ptr == 0x2B)
+ {
+ /* (most) common case 1.3 (iso.org) */
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = 3;
+ oid_ptr++;
+ }
+ else if (*msg_ptr < 40)
+ {
+ *oid_ptr = 0;
+ oid_ptr++;
+ *oid_ptr = *msg_ptr;
+ oid_ptr++;
+ }
+ else if (*msg_ptr < 80)
+ {
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = (*msg_ptr) - 40;
+ oid_ptr++;
+ }
+ else
+ {
+ *oid_ptr = 2;
+ oid_ptr++;
+ *oid_ptr = (*msg_ptr) - 80;
+ oid_ptr++;
+ }
+ oid->len = 2;
+ }
+ else
+ {
+ /* accepting zero length identifiers e.g. for
+ getnext operation. uncommon but valid */
+ return ERR_OK;
+ }
+ len--;
+ if (len > 0)
+ {
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN))
+ {
+ /* sub-identifier uses multiple octets */
+ if (*msg_ptr & 0x80)
+ {
+ s32_t sub_id = 0;
+
+ while ((*msg_ptr & 0x80) && (len > 1))
+ {
+ len--;
+ sub_id = (sub_id << 7) + (*msg_ptr & ~0x80);
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (!(*msg_ptr & 0x80) && (len > 0))
+ {
+ /* last octet sub-identifier */
+ len--;
+ sub_id = (sub_id << 7) + *msg_ptr;
+ *oid_ptr = sub_id;
+ }
+ }
+ else
+ {
+ /* !(*msg_ptr & 0x80) sub-identifier uses single octet */
+ len--;
+ *oid_ptr = *msg_ptr;
+ }
+ if (len > 0)
+ {
+ /* remaining oid bytes available ... */
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ oid_ptr++;
+ oid->len++;
+ }
+ if (len == 0)
+ {
+ /* len == 0, end of oid */
+ return ERR_OK;
+ }
+ else
+ {
+ /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */
+ return ERR_ARG;
+ }
+
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
+ * from incoming message into array.
+ *
+ * @param p points to a pbuf holding an ASN1 coded raw data
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data
+ * @param len length of the coded raw data (zero is valid, e.g. empty string!)
+ * @param raw_len length of the raw return value
+ * @param raw return raw bytes
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ if (len > 0)
+ {
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if (raw_len >= len)
+ {
+ while (len > 1)
+ {
+ /* copy len - 1 octets */
+ len--;
+ *raw = *msg_ptr;
+ raw++;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* copy last octet */
+ *raw = *msg_ptr;
+ return ERR_OK;
+ }
+ else
+ {
+ /* raw_len < len, not enough dst space */
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+ }
+ else
+ {
+ /* len == 0, empty string */
+ return ERR_OK;
+ }
+}
+
+#endif /* LWIP_SNMP */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/snmp/asn1_enc.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,611 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) encoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_asn1.h"
+
+/**
+ * Returns octet count for length.
+ *
+ * @param length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
+{
+ if (length < 0x80U)
+ {
+ *octets_needed = 1;
+ }
+ else if (length < 0x100U)
+ {
+ *octets_needed = 2;
+ }
+ else
+ {
+ *octets_needed = 3;
+ }
+}
+
+/**
+ * Returns octet count for an u32_t.
+ *
+ * @param value
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+void
+snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
+{
+ if (value < 0x80UL)
+ {
+ *octets_needed = 1;
+ }
+ else if (value < 0x8000UL)
+ {
+ *octets_needed = 2;
+ }
+ else if (value < 0x800000UL)
+ {
+ *octets_needed = 3;
+ }
+ else if (value < 0x80000000UL)
+ {
+ *octets_needed = 4;
+ }
+ else
+ {
+ *octets_needed = 5;
+ }
+}
+
+/**
+ * Returns octet count for an s32_t.
+ *
+ * @param value
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed.
+ */
+void
+snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
+{
+ if (value < 0)
+ {
+ value = ~value;
+ }
+ if (value < 0x80L)
+ {
+ *octets_needed = 1;
+ }
+ else if (value < 0x8000L)
+ {
+ *octets_needed = 2;
+ }
+ else if (value < 0x800000L)
+ {
+ *octets_needed = 3;
+ }
+ else
+ {
+ *octets_needed = 4;
+ }
+}
+
+/**
+ * Returns octet count for an object identifier.
+ *
+ * @param ident_len object identifier array length
+ * @param ident points to object identifier array
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed)
+{
+ s32_t sub_id;
+ u8_t cnt;
+
+ cnt = 0;
+ if (ident_len > 1)
+ {
+ /* compressed prefix in one octet */
+ cnt++;
+ ident_len -= 2;
+ ident += 2;
+ }
+ while(ident_len > 0)
+ {
+ ident_len--;
+ sub_id = *ident;
+
+ sub_id >>= 7;
+ cnt++;
+ while(sub_id > 0)
+ {
+ sub_id >>= 7;
+ cnt++;
+ }
+ ident++;
+ }
+ *octets_needed = cnt;
+}
+
+/**
+ * Encodes ASN type field into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param type input ASN1 type
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ *msg_ptr = type;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes host order length field into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode length into
+ * @param ofs points to the offset within the pbuf chain
+ * @param length is the host order length to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (length < 0x80)
+ {
+ *msg_ptr = (u8_t)length;
+ return ERR_OK;
+ }
+ else if (length < 0x100)
+ {
+ *msg_ptr = 0x81;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ *msg_ptr = (u8_t)length;
+ return ERR_OK;
+ }
+ else
+ {
+ u8_t i;
+
+ /* length >= 0x100 && length <= 0xFFFF */
+ *msg_ptr = 0x82;
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (i == 0)
+ {
+ /* least significant length octet */
+ *msg_ptr = (u8_t)length;
+ }
+ else
+ {
+ /* most significant length octet */
+ *msg_ptr = (u8_t)(length >> 8);
+ }
+ }
+ return ERR_OK;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u32t_cnt()
+ */
+err_t
+snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (octets_needed == 5)
+ {
+ /* not enough bits in 'value' add leading 0x00 */
+ octets_needed--;
+ *msg_ptr = 0x00;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ while (octets_needed > 1)
+ {
+ octets_needed--;
+ *msg_ptr = (u8_t)(value >> (octets_needed << 3));
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* (only) one least significant octet */
+ *msg_ptr = (u8_t)value;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes s32_t integer into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
+ * @param value is the host order s32_t value to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_s32t_cnt()
+ */
+err_t
+snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ while (octets_needed > 1)
+ {
+ octets_needed--;
+ *msg_ptr = (u8_t)(value >> (octets_needed << 3));
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* (only) one least significant octet */
+ *msg_ptr = (u8_t)value;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes object identifier into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode oid into
+ * @param ofs points to the offset within the pbuf chain
+ * @param ident_len object identifier array length
+ * @param ident points to object identifier array
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (ident_len > 1)
+ {
+ if ((ident[0] == 1) && (ident[1] == 3))
+ {
+ /* compressed (most common) prefix .iso.org */
+ *msg_ptr = 0x2b;
+ }
+ else
+ {
+ /* calculate prefix */
+ *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]);
+ }
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ ident_len -= 2;
+ ident += 2;
+ }
+ else
+ {
+/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
+ /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
+ return ERR_ARG;
+ }
+ while (ident_len > 0)
+ {
+ s32_t sub_id;
+ u8_t shift, tail;
+
+ ident_len--;
+ sub_id = *ident;
+ tail = 0;
+ shift = 28;
+ while(shift > 0)
+ {
+ u8_t code;
+
+ code = (u8_t)(sub_id >> shift);
+ if ((code != 0) || (tail != 0))
+ {
+ tail = 1;
+ *msg_ptr = code | 0x80;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ shift -= 7;
+ }
+ *msg_ptr = (u8_t)sub_id & 0x7F;
+ if (ident_len > 0)
+ {
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* proceed to next sub-identifier */
+ ident++;
+ }
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode raw data into
+ * @param ofs points to the offset within the pbuf chain
+ * @param raw_len raw data length
+ * @param raw points raw data
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ while (raw_len > 1)
+ {
+ /* copy raw_len - 1 octets */
+ raw_len--;
+ *msg_ptr = *raw;
+ raw++;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (raw_len > 0)
+ {
+ /* copy last or single octet */
+ *msg_ptr = *raw;
+ }
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+#endif /* LWIP_SNMP */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/snmp/mib2.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,4144 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) objects and functions.
+ *
+ * @note the object identifiers for this MIB-2 and private MIB tree
+ * must be kept in sorted ascending order. This to ensure correct getnext operation.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/ip_frag.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/udp.h"
+#include "lwip/snmp_asn1.h"
+#include "lwip/snmp_structs.h"
+#include "netif/etharp.h"
+
+/**
+ * IANA assigned enterprise ID for lwIP is 26381
+ * @see http://www.iana.org/assignments/enterprise-numbers
+ *
+ * @note this enterprise ID is assigned to the lwIP project,
+ * all object identifiers living under this ID are assigned
+ * by the lwIP maintainers (contact Christiaan Simons)!
+ * @note don't change this define, use snmp_set_sysobjid()
+ *
+ * If you need to create your own private MIB you'll need
+ * to apply for your own enterprise ID with IANA:
+ * http://www.iana.org/numbers.html
+ */
+#define SNMP_ENTERPRISE_ID 26381
+#define SNMP_SYSOBJID_LEN 7
+#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID}
+
+#ifndef SNMP_SYSSERVICES
+#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2))
+#endif
+
+#ifndef SNMP_GET_SYSUPTIME
+#define SNMP_GET_SYSUPTIME(sysuptime)
+#endif
+
+static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void system_get_value(struct obj_def *od, u16_t len, void *value);
+static u8_t system_set_test(struct obj_def *od, u16_t len, void *value);
+static void system_set_value(struct obj_def *od, u16_t len, void *value);
+static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void interfaces_get_value(struct obj_def *od, u16_t len, void *value);
+static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ifentry_get_value(struct obj_def *od, u16_t len, void *value);
+#if !SNMP_SAFE_REQUESTS
+static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value);
+static void ifentry_set_value (struct obj_def *od, u16_t len, void *value);
+#endif /* SNMP_SAFE_REQUESTS */
+static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void atentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_get_value(struct obj_def *od, u16_t len, void *value);
+static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value);
+static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void icmp_get_value(struct obj_def *od, u16_t len, void *value);
+#if LWIP_TCP
+static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void tcp_get_value(struct obj_def *od, u16_t len, void *value);
+#ifdef THIS_SEEMS_UNUSED
+static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value);
+#endif
+#endif
+static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void udp_get_value(struct obj_def *od, u16_t len, void *value);
+static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void udpentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void snmp_get_value(struct obj_def *od, u16_t len, void *value);
+static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value);
+static void snmp_set_value(struct obj_def *od, u16_t len, void *value);
+
+
+/* snmp .1.3.6.1.2.1.11 */
+const mib_scalar_node snmp_scalar = {
+ &snmp_get_object_def,
+ &snmp_get_value,
+ &snmp_set_test,
+ &snmp_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t snmp_ids[28] = {
+ 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30
+};
+struct mib_node* const snmp_nodes[28] = {
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar
+};
+const struct mib_array_node snmp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 28,
+ snmp_ids,
+ snmp_nodes
+};
+
+/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */
+/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */
+/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */
+
+/* udp .1.3.6.1.2.1.7 */
+/** index root node for udpTable */
+struct mib_list_rootnode udp_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t udpentry_ids[2] = { 1, 2 };
+struct mib_node* const udpentry_nodes[2] = {
+ (struct mib_node*)&udp_root, (struct mib_node*)&udp_root,
+};
+const struct mib_array_node udpentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 2,
+ udpentry_ids,
+ udpentry_nodes
+};
+
+s32_t udptable_id = 1;
+struct mib_node* udptable_node = (struct mib_node*)&udpentry;
+struct mib_ram_array_node udptable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &udptable_id,
+ &udptable_node
+};
+
+const mib_scalar_node udp_scalar = {
+ &udp_get_object_def,
+ &udp_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 };
+struct mib_node* const udp_nodes[5] = {
+ (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar,
+ (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar,
+ (struct mib_node*)&udptable
+};
+const struct mib_array_node udp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 5,
+ udp_ids,
+ udp_nodes
+};
+
+/* tcp .1.3.6.1.2.1.6 */
+#if LWIP_TCP
+/* only if the TCP protocol is available may implement this group */
+/** index root node for tcpConnTable */
+struct mib_list_rootnode tcpconntree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 };
+struct mib_node* const tcpconnentry_nodes[5] = {
+ (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root,
+ (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root,
+ (struct mib_node*)&tcpconntree_root
+};
+const struct mib_array_node tcpconnentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 5,
+ tcpconnentry_ids,
+ tcpconnentry_nodes
+};
+
+s32_t tcpconntable_id = 1;
+struct mib_node* tcpconntable_node = (struct mib_node*)&tcpconnentry;
+struct mib_ram_array_node tcpconntable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+/** @todo update maxlength when inserting / deleting from table
+ 0 when table is empty, 1 when more than one entry */
+ 0,
+ &tcpconntable_id,
+ &tcpconntable_node
+};
+
+const mib_scalar_node tcp_scalar = {
+ &tcp_get_object_def,
+ &tcp_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+struct mib_node* const tcp_nodes[15] = {
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcpconntable, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar
+};
+const struct mib_array_node tcp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 15,
+ tcp_ids,
+ tcp_nodes
+};
+#endif
+
+/* icmp .1.3.6.1.2.1.5 */
+const mib_scalar_node icmp_scalar = {
+ &icmp_get_object_def,
+ &icmp_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t icmp_ids[26] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 };
+struct mib_node* const icmp_nodes[26] = {
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar
+};
+const struct mib_array_node icmp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 26,
+ icmp_ids,
+ icmp_nodes
+};
+
+/** index root node for ipNetToMediaTable */
+struct mib_list_rootnode ipntomtree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 };
+struct mib_node* const ipntomentry_nodes[4] = {
+ (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root,
+ (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root
+};
+const struct mib_array_node ipntomentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 4,
+ ipntomentry_ids,
+ ipntomentry_nodes
+};
+
+s32_t ipntomtable_id = 1;
+struct mib_node* ipntomtable_node = (struct mib_node*)&ipntomentry;
+struct mib_ram_array_node ipntomtable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &ipntomtable_id,
+ &ipntomtable_node
+};
+
+/** index root node for ipRouteTable */
+struct mib_list_rootnode iprtetree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
+struct mib_node* const iprteentry_nodes[13] = {
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root
+};
+const struct mib_array_node iprteentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 13,
+ iprteentry_ids,
+ iprteentry_nodes
+};
+
+s32_t iprtetable_id = 1;
+struct mib_node* iprtetable_node = (struct mib_node*)&iprteentry;
+struct mib_ram_array_node iprtetable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &iprtetable_id,
+ &iprtetable_node
+};
+
+/** index root node for ipAddrTable */
+struct mib_list_rootnode ipaddrtree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 };
+struct mib_node* const ipaddrentry_nodes[5] = {
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root
+};
+const struct mib_array_node ipaddrentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 5,
+ ipaddrentry_ids,
+ ipaddrentry_nodes
+};
+
+s32_t ipaddrtable_id = 1;
+struct mib_node* ipaddrtable_node = (struct mib_node*)&ipaddrentry;
+struct mib_ram_array_node ipaddrtable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &ipaddrtable_id,
+ &ipaddrtable_node
+};
+
+/* ip .1.3.6.1.2.1.4 */
+const mib_scalar_node ip_scalar = {
+ &ip_get_object_def,
+ &ip_get_value,
+ &ip_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
+struct mib_node* const ip_nodes[23] = {
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ipaddrtable,
+ (struct mib_node*)&iprtetable, (struct mib_node*)&ipntomtable,
+ (struct mib_node*)&ip_scalar
+};
+const struct mib_array_node mib2_ip = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 23,
+ ip_ids,
+ ip_nodes
+};
+
+/** index root node for atTable */
+struct mib_list_rootnode arptree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t atentry_ids[3] = { 1, 2, 3 };
+struct mib_node* const atentry_nodes[3] = {
+ (struct mib_node*)&arptree_root,
+ (struct mib_node*)&arptree_root,
+ (struct mib_node*)&arptree_root
+};
+const struct mib_array_node atentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 3,
+ atentry_ids,
+ atentry_nodes
+};
+
+const s32_t attable_id = 1;
+struct mib_node* const attable_node = (struct mib_node*)&atentry;
+const struct mib_array_node attable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 1,
+ &attable_id,
+ &attable_node
+};
+
+/* at .1.3.6.1.2.1.3 */
+s32_t at_id = 1;
+struct mib_node* mib2_at_node = (struct mib_node*)&attable;
+struct mib_ram_array_node at = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &at_id,
+ &mib2_at_node
+};
+
+/** index root node for ifTable */
+struct mib_list_rootnode iflist_root = {
+ &ifentry_get_object_def,
+ &ifentry_get_value,
+#if SNMP_SAFE_REQUESTS
+ &noleafs_set_test,
+ &noleafs_set_value,
+#else /* SNMP_SAFE_REQUESTS */
+ &ifentry_set_test,
+ &ifentry_set_value,
+#endif /* SNMP_SAFE_REQUESTS */
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 };
+struct mib_node* const ifentry_nodes[22] = {
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root
+};
+const struct mib_array_node ifentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 22,
+ ifentry_ids,
+ ifentry_nodes
+};
+
+s32_t iftable_id = 1;
+struct mib_node* iftable_node = (struct mib_node*)&ifentry;
+struct mib_ram_array_node iftable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &iftable_id,
+ &iftable_node
+};
+
+/* interfaces .1.3.6.1.2.1.2 */
+const mib_scalar_node interfaces_scalar = {
+ &interfaces_get_object_def,
+ &interfaces_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t interfaces_ids[2] = { 1, 2 };
+struct mib_node* const interfaces_nodes[2] = {
+ (struct mib_node*)&interfaces_scalar, (struct mib_node*)&iftable
+};
+const struct mib_array_node interfaces = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 2,
+ interfaces_ids,
+ interfaces_nodes
+};
+
+
+/* 0 1 2 3 4 5 6 */
+/* system .1.3.6.1.2.1.1 */
+const mib_scalar_node sys_tem_scalar = {
+ &system_get_object_def,
+ &system_get_value,
+ &system_set_test,
+ &system_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 };
+struct mib_node* const sys_tem_nodes[7] = {
+ (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
+ (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
+ (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
+ (struct mib_node*)&sys_tem_scalar
+};
+/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */
+const struct mib_array_node sys_tem = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 7,
+ sys_tem_ids,
+ sys_tem_nodes
+};
+
+/* mib-2 .1.3.6.1.2.1 */
+#if LWIP_TCP
+#define MIB2_GROUPS 8
+#else
+#define MIB2_GROUPS 7
+#endif
+const s32_t mib2_ids[MIB2_GROUPS] =
+{
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+#if LWIP_TCP
+ 6,
+#endif
+ 7,
+ 11
+};
+struct mib_node* const mib2_nodes[MIB2_GROUPS] = {
+ (struct mib_node*)&sys_tem,
+ (struct mib_node*)&interfaces,
+ (struct mib_node*)&at,
+ (struct mib_node*)&mib2_ip,
+ (struct mib_node*)&icmp,
+#if LWIP_TCP
+ (struct mib_node*)&tcp,
+#endif
+ (struct mib_node*)&udp,
+ (struct mib_node*)&snmp
+};
+
+const struct mib_array_node mib2 = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ MIB2_GROUPS,
+ mib2_ids,
+ mib2_nodes
+};
+
+/* mgmt .1.3.6.1.2 */
+const s32_t mgmt_ids[1] = { 1 };
+struct mib_node* const mgmt_nodes[1] = { (struct mib_node*)&mib2 };
+const struct mib_array_node mgmt = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 1,
+ mgmt_ids,
+ mgmt_nodes
+};
+
+/* internet .1.3.6.1 */
+#if SNMP_PRIVATE_MIB
+/* When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB. */
+s32_t internet_ids[2] = { 2, 4 };
+struct mib_node* const internet_nodes[2] = { (struct mib_node*)&mgmt, (struct mib_node*)&mib_private };
+const struct mib_array_node internet = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 2,
+ internet_ids,
+ internet_nodes
+};
+#else
+const s32_t internet_ids[1] = { 2 };
+struct mib_node* const internet_nodes[1] = { (struct mib_node*)&mgmt };
+const struct mib_array_node internet = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 1,
+ internet_ids,
+ internet_nodes
+};
+#endif
+
+/** mib-2.system.sysObjectID */
+static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID};
+/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */
+static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}};
+/** mib-2.system.sysServices */
+static const s32_t sysservices = SNMP_SYSSERVICES;
+
+/** mib-2.system.sysDescr */
+static const u8_t sysdescr_len_default = 4;
+static const u8_t sysdescr_default[] = "lwIP";
+static u8_t* sysdescr_len_ptr = (u8_t*)&sysdescr_len_default;
+static u8_t* sysdescr_ptr = (u8_t*)&sysdescr_default[0];
+/** mib-2.system.sysContact */
+static const u8_t syscontact_len_default = 0;
+static const u8_t syscontact_default[] = "";
+static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default;
+static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0];
+/** mib-2.system.sysName */
+static const u8_t sysname_len_default = 8;
+static const u8_t sysname_default[] = "FQDN-unk";
+static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default;
+static u8_t* sysname_ptr = (u8_t*)&sysname_default[0];
+/** mib-2.system.sysLocation */
+static const u8_t syslocation_len_default = 0;
+static const u8_t syslocation_default[] = "";
+static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default;
+static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0];
+/** mib-2.snmp.snmpEnableAuthenTraps */
+static const u8_t snmpenableauthentraps_default = 2; /* disabled */
+static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default;
+
+/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */
+static const struct snmp_obj_id ifspecific = {2, {0, 0}};
+/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */
+static const struct snmp_obj_id iprouteinfo = {2, {0, 0}};
+
+
+
+/* mib-2.system counter(s) */
+static u32_t sysuptime = 0;
+
+/* mib-2.ip counter(s) */
+static u32_t ipinreceives = 0,
+ ipinhdrerrors = 0,
+ ipinaddrerrors = 0,
+ ipforwdatagrams = 0,
+ ipinunknownprotos = 0,
+ ipindiscards = 0,
+ ipindelivers = 0,
+ ipoutrequests = 0,
+ ipoutdiscards = 0,
+ ipoutnoroutes = 0,
+ ipreasmreqds = 0,
+ ipreasmoks = 0,
+ ipreasmfails = 0,
+ ipfragoks = 0,
+ ipfragfails = 0,
+ ipfragcreates = 0,
+ iproutingdiscards = 0;
+/* mib-2.icmp counter(s) */
+static u32_t icmpinmsgs = 0,
+ icmpinerrors = 0,
+ icmpindestunreachs = 0,
+ icmpintimeexcds = 0,
+ icmpinparmprobs = 0,
+ icmpinsrcquenchs = 0,
+ icmpinredirects = 0,
+ icmpinechos = 0,
+ icmpinechoreps = 0,
+ icmpintimestamps = 0,
+ icmpintimestampreps = 0,
+ icmpinaddrmasks = 0,
+ icmpinaddrmaskreps = 0,
+ icmpoutmsgs = 0,
+ icmpouterrors = 0,
+ icmpoutdestunreachs = 0,
+ icmpouttimeexcds = 0,
+ icmpoutparmprobs = 0,
+ icmpoutsrcquenchs = 0,
+ icmpoutredirects = 0,
+ icmpoutechos = 0,
+ icmpoutechoreps = 0,
+ icmpouttimestamps = 0,
+ icmpouttimestampreps = 0,
+ icmpoutaddrmasks = 0,
+ icmpoutaddrmaskreps = 0;
+/* mib-2.tcp counter(s) */
+static u32_t tcpactiveopens = 0,
+ tcppassiveopens = 0,
+ tcpattemptfails = 0,
+ tcpestabresets = 0,
+ tcpinsegs = 0,
+ tcpoutsegs = 0,
+ tcpretranssegs = 0,
+ tcpinerrs = 0,
+ tcpoutrsts = 0;
+/* mib-2.udp counter(s) */
+static u32_t udpindatagrams = 0,
+ udpnoports = 0,
+ udpinerrors = 0,
+ udpoutdatagrams = 0;
+/* mib-2.snmp counter(s) */
+static u32_t snmpinpkts = 0,
+ snmpoutpkts = 0,
+ snmpinbadversions = 0,
+ snmpinbadcommunitynames = 0,
+ snmpinbadcommunityuses = 0,
+ snmpinasnparseerrs = 0,
+ snmpintoobigs = 0,
+ snmpinnosuchnames = 0,
+ snmpinbadvalues = 0,
+ snmpinreadonlys = 0,
+ snmpingenerrs = 0,
+ snmpintotalreqvars = 0,
+ snmpintotalsetvars = 0,
+ snmpingetrequests = 0,
+ snmpingetnexts = 0,
+ snmpinsetrequests = 0,
+ snmpingetresponses = 0,
+ snmpintraps = 0,
+ snmpouttoobigs = 0,
+ snmpoutnosuchnames = 0,
+ snmpoutbadvalues = 0,
+ snmpoutgenerrs = 0,
+ snmpoutgetrequests = 0,
+ snmpoutgetnexts = 0,
+ snmpoutsetrequests = 0,
+ snmpoutgetresponses = 0,
+ snmpouttraps = 0;
+
+
+
+/* prototypes of the following functions are in lwip/src/include/lwip/snmp.h */
+/**
+ * Copy octet string.
+ *
+ * @param dst points to destination
+ * @param src points to source
+ * @param n number of octets to copy.
+ */
+static void ocstrncpy(u8_t *dst, u8_t *src, u16_t n)
+{
+ u16_t i = n;
+ while (i > 0) {
+ i--;
+ *dst++ = *src++;
+ }
+}
+
+/**
+ * Copy object identifier (s32_t) array.
+ *
+ * @param dst points to destination
+ * @param src points to source
+ * @param n number of sub identifiers to copy.
+ */
+void objectidncpy(s32_t *dst, s32_t *src, u8_t n)
+{
+ u8_t i = n;
+ while(i > 0) {
+ i--;
+ *dst++ = *src++;
+ }
+}
+
+/**
+ * Initializes sysDescr pointers.
+ *
+ * @param str if non-NULL then copy str pointer
+ * @param len points to string length, excluding zero terminator
+ */
+void snmp_set_sysdesr(u8_t *str, u8_t *len)
+{
+ if (str != NULL)
+ {
+ sysdescr_ptr = str;
+ sysdescr_len_ptr = len;
+ }
+}
+
+void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid)
+{
+ *oid = &sysobjid;
+}
+
+/**
+ * Initializes sysObjectID value.
+ *
+ * @param oid points to stuct snmp_obj_id to copy
+ */
+void snmp_set_sysobjid(struct snmp_obj_id *oid)
+{
+ sysobjid = *oid;
+}
+
+/**
+ * Must be called at regular 10 msec interval from a timer interrupt
+ * or signal handler depending on your runtime environment.
+ */
+void snmp_inc_sysuptime(void)
+{
+ sysuptime++;
+}
+
+void snmp_add_sysuptime(u32_t value)
+{
+ sysuptime+=value;
+}
+
+void snmp_get_sysuptime(u32_t *value)
+{
+ SNMP_GET_SYSUPTIME(sysuptime);
+ *value = sysuptime;
+}
+
+/**
+ * Initializes sysContact pointers,
+ * e.g. ptrs to non-volatile memory external to lwIP.
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator
+ */
+void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen)
+{
+ if (ocstr != NULL)
+ {
+ syscontact_ptr = ocstr;
+ syscontact_len_ptr = ocstrlen;
+ }
+}
+
+/**
+ * Initializes sysName pointers,
+ * e.g. ptrs to non-volatile memory external to lwIP.
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator
+ */
+void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen)
+{
+ if (ocstr != NULL)
+ {
+ sysname_ptr = ocstr;
+ sysname_len_ptr = ocstrlen;
+ }
+}
+
+/**
+ * Initializes sysLocation pointers,
+ * e.g. ptrs to non-volatile memory external to lwIP.
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator
+ */
+void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen)
+{
+ if (ocstr != NULL)
+ {
+ syslocation_ptr = ocstr;
+ syslocation_len_ptr = ocstrlen;
+ }
+}
+
+
+void snmp_add_ifinoctets(struct netif *ni, u32_t value)
+{
+ ni->ifinoctets += value;
+}
+
+void snmp_inc_ifinucastpkts(struct netif *ni)
+{
+ (ni->ifinucastpkts)++;
+}
+
+void snmp_inc_ifinnucastpkts(struct netif *ni)
+{
+ (ni->ifinnucastpkts)++;
+}
+
+void snmp_inc_ifindiscards(struct netif *ni)
+{
+ (ni->ifindiscards)++;
+}
+
+void snmp_add_ifoutoctets(struct netif *ni, u32_t value)
+{
+ ni->ifoutoctets += value;
+}
+
+void snmp_inc_ifoutucastpkts(struct netif *ni)
+{
+ (ni->ifoutucastpkts)++;
+}
+
+void snmp_inc_ifoutnucastpkts(struct netif *ni)
+{
+ (ni->ifoutnucastpkts)++;
+}
+
+void snmp_inc_ifoutdiscards(struct netif *ni)
+{
+ (ni->ifoutdiscards)++;
+}
+
+void snmp_inc_iflist(void)
+{
+ struct mib_list_node *if_node = NULL;
+
+ snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node);
+ /* enable getnext traversal on filled table */
+ iftable.maxlength = 1;
+}
+
+void snmp_dec_iflist(void)
+{
+ snmp_mib_node_delete(&iflist_root, iflist_root.tail);
+ /* disable getnext traversal on empty table */
+ if(iflist_root.count == 0) iftable.maxlength = 0;
+}
+
+/**
+ * Inserts ARP table indexes (.xIfIndex.xNetAddress)
+ * into arp table index trees (both atTable and ipNetToMediaTable).
+ */
+void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip)
+{
+ struct mib_list_rootnode *at_rn;
+ struct mib_list_node *at_node;
+ s32_t arpidx[5];
+ u8_t level, tree;
+
+ LWIP_ASSERT("ni != NULL", ni != NULL);
+ snmp_netiftoifindex(ni, &arpidx[0]);
+ snmp_iptooid(ip, &arpidx[1]);
+
+ for (tree = 0; tree < 2; tree++)
+ {
+ if (tree == 0)
+ {
+ at_rn = &arptree_root;
+ }
+ else
+ {
+ at_rn = &ipntomtree_root;
+ }
+ for (level = 0; level < 5; level++)
+ {
+ at_node = NULL;
+ snmp_mib_node_insert(at_rn, arpidx[level], &at_node);
+ if ((level != 4) && (at_node != NULL))
+ {
+ if (at_node->nptr == NULL)
+ {
+ at_rn = snmp_mib_lrn_alloc();
+ at_node->nptr = (struct mib_node*)at_rn;
+ if (at_rn != NULL)
+ {
+ if (level == 3)
+ {
+ if (tree == 0)
+ {
+ at_rn->get_object_def = atentry_get_object_def;
+ at_rn->get_value = atentry_get_value;
+ }
+ else
+ {
+ at_rn->get_object_def = ip_ntomentry_get_object_def;
+ at_rn->get_value = ip_ntomentry_get_value;
+ }
+ at_rn->set_test = noleafs_set_test;
+ at_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* at_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ at_rn = (struct mib_list_rootnode*)at_node->nptr;
+ }
+ }
+ }
+ }
+ /* enable getnext traversal on filled tables */
+ at.maxlength = 1;
+ ipntomtable.maxlength = 1;
+}
+
+/**
+ * Removes ARP table indexes (.xIfIndex.xNetAddress)
+ * from arp table index trees.
+ */
+void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip)
+{
+ struct mib_list_rootnode *at_rn, *next, *del_rn[5];
+ struct mib_list_node *at_n, *del_n[5];
+ s32_t arpidx[5];
+ u8_t fc, tree, level, del_cnt;
+
+ snmp_netiftoifindex(ni, &arpidx[0]);
+ snmp_iptooid(ip, &arpidx[1]);
+
+ for (tree = 0; tree < 2; tree++)
+ {
+ /* mark nodes for deletion */
+ if (tree == 0)
+ {
+ at_rn = &arptree_root;
+ }
+ else
+ {
+ at_rn = &ipntomtree_root;
+ }
+ level = 0;
+ del_cnt = 0;
+ while ((level < 5) && (at_rn != NULL))
+ {
+ fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n);
+ if (fc == 0)
+ {
+ /* arpidx[level] does not exist */
+ del_cnt = 0;
+ at_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = at_rn;
+ del_n[del_cnt] = at_n;
+ del_cnt++;
+ at_rn = (struct mib_list_rootnode*)(at_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ at_rn = (struct mib_list_rootnode*)(at_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ at_rn = del_rn[del_cnt];
+ at_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(at_rn, at_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ }
+ /* disable getnext traversal on empty tables */
+ if(arptree_root.count == 0) at.maxlength = 0;
+ if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0;
+}
+
+void snmp_inc_ipinreceives(void)
+{
+ ipinreceives++;
+}
+
+void snmp_inc_ipinhdrerrors(void)
+{
+ ipinhdrerrors++;
+}
+
+void snmp_inc_ipinaddrerrors(void)
+{
+ ipinaddrerrors++;
+}
+
+void snmp_inc_ipforwdatagrams(void)
+{
+ ipforwdatagrams++;
+}
+
+void snmp_inc_ipinunknownprotos(void)
+{
+ ipinunknownprotos++;
+}
+
+void snmp_inc_ipindiscards(void)
+{
+ ipindiscards++;
+}
+
+void snmp_inc_ipindelivers(void)
+{
+ ipindelivers++;
+}
+
+void snmp_inc_ipoutrequests(void)
+{
+ ipoutrequests++;
+}
+
+void snmp_inc_ipoutdiscards(void)
+{
+ ipoutdiscards++;
+}
+
+void snmp_inc_ipoutnoroutes(void)
+{
+ ipoutnoroutes++;
+}
+
+void snmp_inc_ipreasmreqds(void)
+{
+ ipreasmreqds++;
+}
+
+void snmp_inc_ipreasmoks(void)
+{
+ ipreasmoks++;
+}
+
+void snmp_inc_ipreasmfails(void)
+{
+ ipreasmfails++;
+}
+
+void snmp_inc_ipfragoks(void)
+{
+ ipfragoks++;
+}
+
+void snmp_inc_ipfragfails(void)
+{
+ ipfragfails++;
+}
+
+void snmp_inc_ipfragcreates(void)
+{
+ ipfragcreates++;
+}
+
+void snmp_inc_iproutingdiscards(void)
+{
+ iproutingdiscards++;
+}
+
+/**
+ * Inserts ipAddrTable indexes (.ipAdEntAddr)
+ * into index tree.
+ */
+void snmp_insert_ipaddridx_tree(struct netif *ni)
+{
+ struct mib_list_rootnode *ipa_rn;
+ struct mib_list_node *ipa_node;
+ s32_t ipaddridx[4];
+ u8_t level;
+
+ LWIP_ASSERT("ni != NULL", ni != NULL);
+ snmp_iptooid(&ni->ip_addr, &ipaddridx[0]);
+
+ level = 0;
+ ipa_rn = &ipaddrtree_root;
+ while (level < 4)
+ {
+ ipa_node = NULL;
+ snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node);
+ if ((level != 3) && (ipa_node != NULL))
+ {
+ if (ipa_node->nptr == NULL)
+ {
+ ipa_rn = snmp_mib_lrn_alloc();
+ ipa_node->nptr = (struct mib_node*)ipa_rn;
+ if (ipa_rn != NULL)
+ {
+ if (level == 2)
+ {
+ ipa_rn->get_object_def = ip_addrentry_get_object_def;
+ ipa_rn->get_value = ip_addrentry_get_value;
+ ipa_rn->set_test = noleafs_set_test;
+ ipa_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* ipa_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr;
+ }
+ }
+ level++;
+ }
+ /* enable getnext traversal on filled table */
+ ipaddrtable.maxlength = 1;
+}
+
+/**
+ * Removes ipAddrTable indexes (.ipAdEntAddr)
+ * from index tree.
+ */
+void snmp_delete_ipaddridx_tree(struct netif *ni)
+{
+ struct mib_list_rootnode *ipa_rn, *next, *del_rn[4];
+ struct mib_list_node *ipa_n, *del_n[4];
+ s32_t ipaddridx[4];
+ u8_t fc, level, del_cnt;
+
+ LWIP_ASSERT("ni != NULL", ni != NULL);
+ snmp_iptooid(&ni->ip_addr, &ipaddridx[0]);
+
+ /* mark nodes for deletion */
+ level = 0;
+ del_cnt = 0;
+ ipa_rn = &ipaddrtree_root;
+ while ((level < 4) && (ipa_rn != NULL))
+ {
+ fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n);
+ if (fc == 0)
+ {
+ /* ipaddridx[level] does not exist */
+ del_cnt = 0;
+ ipa_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = ipa_rn;
+ del_n[del_cnt] = ipa_n;
+ del_cnt++;
+ ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ ipa_rn = del_rn[del_cnt];
+ ipa_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(ipa_rn, ipa_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ /* disable getnext traversal on empty table */
+ if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0;
+}
+
+/**
+ * Inserts ipRouteTable indexes (.ipRouteDest)
+ * into index tree.
+ *
+ * @param dflt non-zero for the default rte, zero for network rte
+ * @param ni points to network interface for this rte
+ *
+ * @todo record sysuptime for _this_ route when it is installed
+ * (needed for ipRouteAge) in the netif.
+ */
+void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni)
+{
+ u8_t insert = 0;
+ ip_addr_t dst;
+
+ if (dflt != 0)
+ {
+ /* the default route 0.0.0.0 */
+ ip_addr_set_any(&dst);
+ insert = 1;
+ }
+ else
+ {
+ /* route to the network address */
+ ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask);
+ /* exclude 0.0.0.0 network (reserved for default rte) */
+ if (!ip_addr_isany(&dst)) {
+ insert = 1;
+ }
+ }
+ if (insert)
+ {
+ struct mib_list_rootnode *iprte_rn;
+ struct mib_list_node *iprte_node;
+ s32_t iprteidx[4];
+ u8_t level;
+
+ snmp_iptooid(&dst, &iprteidx[0]);
+ level = 0;
+ iprte_rn = &iprtetree_root;
+ while (level < 4)
+ {
+ iprte_node = NULL;
+ snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node);
+ if ((level != 3) && (iprte_node != NULL))
+ {
+ if (iprte_node->nptr == NULL)
+ {
+ iprte_rn = snmp_mib_lrn_alloc();
+ iprte_node->nptr = (struct mib_node*)iprte_rn;
+ if (iprte_rn != NULL)
+ {
+ if (level == 2)
+ {
+ iprte_rn->get_object_def = ip_rteentry_get_object_def;
+ iprte_rn->get_value = ip_rteentry_get_value;
+ iprte_rn->set_test = noleafs_set_test;
+ iprte_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* iprte_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr;
+ }
+ }
+ level++;
+ }
+ }
+ /* enable getnext traversal on filled table */
+ iprtetable.maxlength = 1;
+}
+
+/**
+ * Removes ipRouteTable indexes (.ipRouteDest)
+ * from index tree.
+ *
+ * @param dflt non-zero for the default rte, zero for network rte
+ * @param ni points to network interface for this rte or NULL
+ * for default route to be removed.
+ */
+void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni)
+{
+ u8_t del = 0;
+ ip_addr_t dst;
+
+ if (dflt != 0)
+ {
+ /* the default route 0.0.0.0 */
+ ip_addr_set_any(&dst);
+ del = 1;
+ }
+ else
+ {
+ /* route to the network address */
+ ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask);
+ /* exclude 0.0.0.0 network (reserved for default rte) */
+ if (!ip_addr_isany(&dst)) {
+ del = 1;
+ }
+ }
+ if (del)
+ {
+ struct mib_list_rootnode *iprte_rn, *next, *del_rn[4];
+ struct mib_list_node *iprte_n, *del_n[4];
+ s32_t iprteidx[4];
+ u8_t fc, level, del_cnt;
+
+ snmp_iptooid(&dst, &iprteidx[0]);
+ /* mark nodes for deletion */
+ level = 0;
+ del_cnt = 0;
+ iprte_rn = &iprtetree_root;
+ while ((level < 4) && (iprte_rn != NULL))
+ {
+ fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n);
+ if (fc == 0)
+ {
+ /* iprteidx[level] does not exist */
+ del_cnt = 0;
+ iprte_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = iprte_rn;
+ del_n[del_cnt] = iprte_n;
+ del_cnt++;
+ iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ iprte_rn = del_rn[del_cnt];
+ iprte_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(iprte_rn, iprte_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ }
+ /* disable getnext traversal on empty table */
+ if (iprtetree_root.count == 0) iprtetable.maxlength = 0;
+}
+
+
+void snmp_inc_icmpinmsgs(void)
+{
+ icmpinmsgs++;
+}
+
+void snmp_inc_icmpinerrors(void)
+{
+ icmpinerrors++;
+}
+
+void snmp_inc_icmpindestunreachs(void)
+{
+ icmpindestunreachs++;
+}
+
+void snmp_inc_icmpintimeexcds(void)
+{
+ icmpintimeexcds++;
+}
+
+void snmp_inc_icmpinparmprobs(void)
+{
+ icmpinparmprobs++;
+}
+
+void snmp_inc_icmpinsrcquenchs(void)
+{
+ icmpinsrcquenchs++;
+}
+
+void snmp_inc_icmpinredirects(void)
+{
+ icmpinredirects++;
+}
+
+void snmp_inc_icmpinechos(void)
+{
+ icmpinechos++;
+}
+
+void snmp_inc_icmpinechoreps(void)
+{
+ icmpinechoreps++;
+}
+
+void snmp_inc_icmpintimestamps(void)
+{
+ icmpintimestamps++;
+}
+
+void snmp_inc_icmpintimestampreps(void)
+{
+ icmpintimestampreps++;
+}
+
+void snmp_inc_icmpinaddrmasks(void)
+{
+ icmpinaddrmasks++;
+}
+
+void snmp_inc_icmpinaddrmaskreps(void)
+{
+ icmpinaddrmaskreps++;
+}
+
+void snmp_inc_icmpoutmsgs(void)
+{
+ icmpoutmsgs++;
+}
+
+void snmp_inc_icmpouterrors(void)
+{
+ icmpouterrors++;
+}
+
+void snmp_inc_icmpoutdestunreachs(void)
+{
+ icmpoutdestunreachs++;
+}
+
+void snmp_inc_icmpouttimeexcds(void)
+{
+ icmpouttimeexcds++;
+}
+
+void snmp_inc_icmpoutparmprobs(void)
+{
+ icmpoutparmprobs++;
+}
+
+void snmp_inc_icmpoutsrcquenchs(void)
+{
+ icmpoutsrcquenchs++;
+}
+
+void snmp_inc_icmpoutredirects(void)
+{
+ icmpoutredirects++;
+}
+
+void snmp_inc_icmpoutechos(void)
+{
+ icmpoutechos++;
+}
+
+void snmp_inc_icmpoutechoreps(void)
+{
+ icmpoutechoreps++;
+}
+
+void snmp_inc_icmpouttimestamps(void)
+{
+ icmpouttimestamps++;
+}
+
+void snmp_inc_icmpouttimestampreps(void)
+{
+ icmpouttimestampreps++;
+}
+
+void snmp_inc_icmpoutaddrmasks(void)
+{
+ icmpoutaddrmasks++;
+}
+
+void snmp_inc_icmpoutaddrmaskreps(void)
+{
+ icmpoutaddrmaskreps++;
+}
+
+void snmp_inc_tcpactiveopens(void)
+{
+ tcpactiveopens++;
+}
+
+void snmp_inc_tcppassiveopens(void)
+{
+ tcppassiveopens++;
+}
+
+void snmp_inc_tcpattemptfails(void)
+{
+ tcpattemptfails++;
+}
+
+void snmp_inc_tcpestabresets(void)
+{
+ tcpestabresets++;
+}
+
+void snmp_inc_tcpinsegs(void)
+{
+ tcpinsegs++;
+}
+
+void snmp_inc_tcpoutsegs(void)
+{
+ tcpoutsegs++;
+}
+
+void snmp_inc_tcpretranssegs(void)
+{
+ tcpretranssegs++;
+}
+
+void snmp_inc_tcpinerrs(void)
+{
+ tcpinerrs++;
+}
+
+void snmp_inc_tcpoutrsts(void)
+{
+ tcpoutrsts++;
+}
+
+void snmp_inc_udpindatagrams(void)
+{
+ udpindatagrams++;
+}
+
+void snmp_inc_udpnoports(void)
+{
+ udpnoports++;
+}
+
+void snmp_inc_udpinerrors(void)
+{
+ udpinerrors++;
+}
+
+void snmp_inc_udpoutdatagrams(void)
+{
+ udpoutdatagrams++;
+}
+
+/**
+ * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort)
+ * into index tree.
+ */
+void snmp_insert_udpidx_tree(struct udp_pcb *pcb)
+{
+ struct mib_list_rootnode *udp_rn;
+ struct mib_list_node *udp_node;
+ s32_t udpidx[5];
+ u8_t level;
+
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+ snmp_iptooid(&pcb->local_ip, &udpidx[0]);
+ udpidx[4] = pcb->local_port;
+
+ udp_rn = &udp_root;
+ for (level = 0; level < 5; level++)
+ {
+ udp_node = NULL;
+ snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node);
+ if ((level != 4) && (udp_node != NULL))
+ {
+ if (udp_node->nptr == NULL)
+ {
+ udp_rn = snmp_mib_lrn_alloc();
+ udp_node->nptr = (struct mib_node*)udp_rn;
+ if (udp_rn != NULL)
+ {
+ if (level == 3)
+ {
+ udp_rn->get_object_def = udpentry_get_object_def;
+ udp_rn->get_value = udpentry_get_value;
+ udp_rn->set_test = noleafs_set_test;
+ udp_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* udp_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ udp_rn = (struct mib_list_rootnode*)udp_node->nptr;
+ }
+ }
+ }
+ udptable.maxlength = 1;
+}
+
+/**
+ * Removes udpTable indexes (.udpLocalAddress.udpLocalPort)
+ * from index tree.
+ */
+void snmp_delete_udpidx_tree(struct udp_pcb *pcb)
+{
+ struct udp_pcb *npcb;
+ struct mib_list_rootnode *udp_rn, *next, *del_rn[5];
+ struct mib_list_node *udp_n, *del_n[5];
+ s32_t udpidx[5];
+ u8_t bindings, fc, level, del_cnt;
+
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+ snmp_iptooid(&pcb->local_ip, &udpidx[0]);
+ udpidx[4] = pcb->local_port;
+
+ /* count PCBs for a given binding
+ (e.g. when reusing ports or for temp output PCBs) */
+ bindings = 0;
+ npcb = udp_pcbs;
+ while ((npcb != NULL))
+ {
+ if (ip_addr_cmp(&npcb->local_ip, &pcb->local_ip) &&
+ (npcb->local_port == udpidx[4]))
+ {
+ bindings++;
+ }
+ npcb = npcb->next;
+ }
+ if (bindings == 1)
+ {
+ /* selectively remove */
+ /* mark nodes for deletion */
+ level = 0;
+ del_cnt = 0;
+ udp_rn = &udp_root;
+ while ((level < 5) && (udp_rn != NULL))
+ {
+ fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n);
+ if (fc == 0)
+ {
+ /* udpidx[level] does not exist */
+ del_cnt = 0;
+ udp_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = udp_rn;
+ del_n[del_cnt] = udp_n;
+ del_cnt++;
+ udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ udp_rn = del_rn[del_cnt];
+ udp_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(udp_rn, udp_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ }
+ /* disable getnext traversal on empty table */
+ if (udp_root.count == 0) udptable.maxlength = 0;
+}
+
+
+void snmp_inc_snmpinpkts(void)
+{
+ snmpinpkts++;
+}
+
+void snmp_inc_snmpoutpkts(void)
+{
+ snmpoutpkts++;
+}
+
+void snmp_inc_snmpinbadversions(void)
+{
+ snmpinbadversions++;
+}
+
+void snmp_inc_snmpinbadcommunitynames(void)
+{
+ snmpinbadcommunitynames++;
+}
+
+void snmp_inc_snmpinbadcommunityuses(void)
+{
+ snmpinbadcommunityuses++;
+}
+
+void snmp_inc_snmpinasnparseerrs(void)
+{
+ snmpinasnparseerrs++;
+}
+
+void snmp_inc_snmpintoobigs(void)
+{
+ snmpintoobigs++;
+}
+
+void snmp_inc_snmpinnosuchnames(void)
+{
+ snmpinnosuchnames++;
+}
+
+void snmp_inc_snmpinbadvalues(void)
+{
+ snmpinbadvalues++;
+}
+
+void snmp_inc_snmpinreadonlys(void)
+{
+ snmpinreadonlys++;
+}
+
+void snmp_inc_snmpingenerrs(void)
+{
+ snmpingenerrs++;
+}
+
+void snmp_add_snmpintotalreqvars(u8_t value)
+{
+ snmpintotalreqvars += value;
+}
+
+void snmp_add_snmpintotalsetvars(u8_t value)
+{
+ snmpintotalsetvars += value;
+}
+
+void snmp_inc_snmpingetrequests(void)
+{
+ snmpingetrequests++;
+}
+
+void snmp_inc_snmpingetnexts(void)
+{
+ snmpingetnexts++;
+}
+
+void snmp_inc_snmpinsetrequests(void)
+{
+ snmpinsetrequests++;
+}
+
+void snmp_inc_snmpingetresponses(void)
+{
+ snmpingetresponses++;
+}
+
+void snmp_inc_snmpintraps(void)
+{
+ snmpintraps++;
+}
+
+void snmp_inc_snmpouttoobigs(void)
+{
+ snmpouttoobigs++;
+}
+
+void snmp_inc_snmpoutnosuchnames(void)
+{
+ snmpoutnosuchnames++;
+}
+
+void snmp_inc_snmpoutbadvalues(void)
+{
+ snmpoutbadvalues++;
+}
+
+void snmp_inc_snmpoutgenerrs(void)
+{
+ snmpoutgenerrs++;
+}
+
+void snmp_inc_snmpoutgetrequests(void)
+{
+ snmpoutgetrequests++;
+}
+
+void snmp_inc_snmpoutgetnexts(void)
+{
+ snmpoutgetnexts++;
+}
+
+void snmp_inc_snmpoutsetrequests(void)
+{
+ snmpoutsetrequests++;
+}
+
+void snmp_inc_snmpoutgetresponses(void)
+{
+ snmpoutgetresponses++;
+}
+
+void snmp_inc_snmpouttraps(void)
+{
+ snmpouttraps++;
+}
+
+void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid)
+{
+ *oid = &snmpgrp_id;
+}
+
+void snmp_set_snmpenableauthentraps(u8_t *value)
+{
+ if (value != NULL)
+ {
+ snmpenableauthentraps_ptr = value;
+ }
+}
+
+void snmp_get_snmpenableauthentraps(u8_t *value)
+{
+ *value = *snmpenableauthentraps_ptr;
+}
+
+void
+noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ LWIP_UNUSED_ARG(ident_len);
+ LWIP_UNUSED_ARG(ident);
+ od->instance = MIB_OBJECT_NONE;
+}
+
+void
+noleafs_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(od);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+}
+
+u8_t
+noleafs_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(od);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+ /* can't set */
+ return 0;
+}
+
+void
+noleafs_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(od);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+}
+
+
+/**
+ * Returns systems object definitions.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param od points to object definition.
+ */
+static void
+system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id));
+ switch (id)
+ {
+ case 1: /* sysDescr */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *sysdescr_len_ptr;
+ break;
+ case 2: /* sysObjectID */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
+ od->v_len = sysobjid.len * sizeof(s32_t);
+ break;
+ case 3: /* sysUpTime */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 4: /* sysContact */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *syscontact_len_ptr;
+ break;
+ case 5: /* sysName */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *sysname_len_ptr;
+ break;
+ case 6: /* sysLocation */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = *syslocation_len_ptr;
+ break;
+ case 7: /* sysServices */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+/**
+ * Returns system object value.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value into.
+ */
+static void
+system_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* sysDescr */
+ ocstrncpy((u8_t*)value, sysdescr_ptr, len);
+ break;
+ case 2: /* sysObjectID */
+ objectidncpy((s32_t*)value, (s32_t*)sysobjid.id, (u8_t)(len / sizeof(s32_t)));
+ break;
+ case 3: /* sysUpTime */
+ {
+ snmp_get_sysuptime((u32_t*)value);
+ }
+ break;
+ case 4: /* sysContact */
+ ocstrncpy((u8_t*)value, syscontact_ptr, len);
+ break;
+ case 5: /* sysName */
+ ocstrncpy((u8_t*)value, sysname_ptr, len);
+ break;
+ case 6: /* sysLocation */
+ ocstrncpy((u8_t*)value, syslocation_ptr, len);
+ break;
+ case 7: /* sysServices */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = sysservices;
+ }
+ break;
+ };
+}
+
+static u8_t
+system_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id, set_ok;
+
+ LWIP_UNUSED_ARG(value);
+ set_ok = 0;
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 4: /* sysContact */
+ if ((syscontact_ptr != syscontact_default) &&
+ (len <= 255))
+ {
+ set_ok = 1;
+ }
+ break;
+ case 5: /* sysName */
+ if ((sysname_ptr != sysname_default) &&
+ (len <= 255))
+ {
+ set_ok = 1;
+ }
+ break;
+ case 6: /* sysLocation */
+ if ((syslocation_ptr != syslocation_default) &&
+ (len <= 255))
+ {
+ set_ok = 1;
+ }
+ break;
+ };
+ return set_ok;
+}
+
+static void
+system_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_ASSERT("invalid len", len <= 0xff);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 4: /* sysContact */
+ ocstrncpy(syscontact_ptr, (u8_t*)value, len);
+ *syscontact_len_ptr = (u8_t)len;
+ break;
+ case 5: /* sysName */
+ ocstrncpy(sysname_ptr, (u8_t*)value, len);
+ *sysname_len_ptr = (u8_t)len;
+ break;
+ case 6: /* sysLocation */
+ ocstrncpy(syslocation_ptr, (u8_t*)value, len);
+ *syslocation_len_ptr = (u8_t)len;
+ break;
+ };
+}
+
+/**
+ * Returns interfaces.ifnumber object definition.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.index
+ * @param od points to object definition.
+ */
+static void
+interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+/**
+ * Returns interfaces.ifnumber object value.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value into.
+ */
+static void
+interfaces_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(len);
+ if (od->id_inst_ptr[0] == 1)
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = iflist_root.count;
+ }
+}
+
+/**
+ * Returns ifentry object definitions.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.index
+ * @param od points to object definition.
+ */
+static void
+ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id));
+ switch (id)
+ {
+ case 1: /* ifIndex */
+ case 3: /* ifType */
+ case 4: /* ifMtu */
+ case 8: /* ifOperStatus */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* ifDescr */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ /** @todo this should be some sort of sizeof(struct netif.name) */
+ od->v_len = 2;
+ break;
+ case 5: /* ifSpeed */
+ case 21: /* ifOutQLen */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 6: /* ifPhysAddress */
+ {
+ struct netif *netif;
+
+ snmp_ifindextonetif(ident[1], &netif);
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = netif->hwaddr_len;
+ }
+ break;
+ case 7: /* ifAdminStatus */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 9: /* ifLastChange */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 10: /* ifInOctets */
+ case 11: /* ifInUcastPkts */
+ case 12: /* ifInNUcastPkts */
+ case 13: /* ifInDiscarts */
+ case 14: /* ifInErrors */
+ case 15: /* ifInUnkownProtos */
+ case 16: /* ifOutOctets */
+ case 17: /* ifOutUcastPkts */
+ case 18: /* ifOutNUcastPkts */
+ case 19: /* ifOutDiscarts */
+ case 20: /* ifOutErrors */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 22: /* ifSpecific */
+ /** @note returning zeroDotZero (0.0) no media specific MIB support */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
+ od->v_len = ifspecific.len * sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+/**
+ * Returns ifentry object value.
+ *
+ * @param ident_len the address length (2)
+ * @param ident points to objectname.0 (object id trailer)
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value into.
+ */
+static void
+ifentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ u8_t id;
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ifIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = od->id_inst_ptr[1];
+ }
+ break;
+ case 2: /* ifDescr */
+ ocstrncpy((u8_t*)value, (u8_t*)netif->name, len);
+ break;
+ case 3: /* ifType */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = netif->link_type;
+ }
+ break;
+ case 4: /* ifMtu */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = netif->mtu;
+ }
+ break;
+ case 5: /* ifSpeed */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->link_speed;
+ }
+ break;
+ case 6: /* ifPhysAddress */
+ ocstrncpy((u8_t*)value, netif->hwaddr, len);
+ break;
+ case 7: /* ifAdminStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (netif_is_up(netif))
+ {
+ if (netif_is_link_up(netif))
+ {
+ *sint_ptr = 1; /* up */
+ }
+ else
+ {
+ *sint_ptr = 7; /* lowerLayerDown */
+ }
+ }
+ else
+ {
+ *sint_ptr = 2; /* down */
+ }
+ }
+ break;
+ case 8: /* ifOperStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (netif_is_up(netif))
+ {
+ *sint_ptr = 1;
+ }
+ else
+ {
+ *sint_ptr = 2;
+ }
+ }
+ break;
+ case 9: /* ifLastChange */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ts;
+ }
+ break;
+ case 10: /* ifInOctets */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifinoctets;
+ }
+ break;
+ case 11: /* ifInUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifinucastpkts;
+ }
+ break;
+ case 12: /* ifInNUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifinnucastpkts;
+ }
+ break;
+ case 13: /* ifInDiscarts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifindiscards;
+ }
+ break;
+ case 14: /* ifInErrors */
+ case 15: /* ifInUnkownProtos */
+ /** @todo add these counters! */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = 0;
+ }
+ break;
+ case 16: /* ifOutOctets */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutoctets;
+ }
+ break;
+ case 17: /* ifOutUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutucastpkts;
+ }
+ break;
+ case 18: /* ifOutNUcastPkts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutnucastpkts;
+ }
+ break;
+ case 19: /* ifOutDiscarts */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = netif->ifoutdiscards;
+ }
+ break;
+ case 20: /* ifOutErrors */
+ /** @todo add this counter! */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = 0;
+ }
+ break;
+ case 21: /* ifOutQLen */
+ /** @todo figure out if this must be 0 (no queue) or 1? */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = 0;
+ }
+ break;
+ case 22: /* ifSpecific */
+ objectidncpy((s32_t*)value, (s32_t*)ifspecific.id, (u8_t)(len / sizeof(s32_t)));
+ break;
+ };
+}
+
+#if !SNMP_SAFE_REQUESTS
+static u8_t
+ifentry_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ u8_t id, set_ok;
+ LWIP_UNUSED_ARG(len);
+
+ set_ok = 0;
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 7: /* ifAdminStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (*sint_ptr == 1 || *sint_ptr == 2)
+ set_ok = 1;
+ }
+ break;
+ }
+ return set_ok;
+}
+
+static void
+ifentry_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ u8_t id;
+ LWIP_UNUSED_ARG(len);
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 7: /* ifAdminStatus */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ if (*sint_ptr == 1)
+ {
+ netif_set_up(netif);
+ }
+ else if (*sint_ptr == 2)
+ {
+ netif_set_down(netif);
+ }
+ }
+ break;
+ }
+}
+#endif /* SNMP_SAFE_REQUESTS */
+
+/**
+ * Returns atentry object definitions.
+ *
+ * @param ident_len the address length (6)
+ * @param ident points to objectname.atifindex.atnetaddress
+ * @param od points to object definition.
+ */
+static void
+atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (5) */
+ ident_len += 5;
+ ident -= 5;
+
+ if (ident_len == 6)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ switch (ident[0])
+ {
+ case 1: /* atIfIndex */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* atPhysAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = 6; /** @todo try to use netif::hwaddr_len */
+ break;
+ case 3: /* atNetAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+atentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+#if LWIP_ARP
+ u8_t id;
+ struct eth_addr* ethaddr_ret;
+ ip_addr_t* ipaddr_ret;
+#endif /* LWIP_ARP */
+ ip_addr_t ip;
+ struct netif *netif;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ snmp_oidtoip(&od->id_inst_ptr[2], &ip);
+
+#if LWIP_ARP /** @todo implement a netif_find_addr */
+ if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* atIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = od->id_inst_ptr[1];
+ }
+ break;
+ case 2: /* atPhysAddress */
+ {
+ struct eth_addr *dst = (struct eth_addr*)value;
+
+ *dst = *ethaddr_ret;
+ }
+ break;
+ case 3: /* atNetAddress */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ *dst = *ipaddr_ret;
+ }
+ break;
+ }
+ }
+#endif /* LWIP_ARP */
+}
+
+static void
+ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id));
+ switch (id)
+ {
+ case 1: /* ipForwarding */
+ case 2: /* ipDefaultTTL */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 3: /* ipInReceives */
+ case 4: /* ipInHdrErrors */
+ case 5: /* ipInAddrErrors */
+ case 6: /* ipForwDatagrams */
+ case 7: /* ipInUnknownProtos */
+ case 8: /* ipInDiscards */
+ case 9: /* ipInDelivers */
+ case 10: /* ipOutRequests */
+ case 11: /* ipOutDiscards */
+ case 12: /* ipOutNoRoutes */
+ case 14: /* ipReasmReqds */
+ case 15: /* ipReasmOKs */
+ case 16: /* ipReasmFails */
+ case 17: /* ipFragOKs */
+ case 18: /* ipFragFails */
+ case 19: /* ipFragCreates */
+ case 23: /* ipRoutingDiscards */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 13: /* ipReasmTimeout */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipForwarding */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+#if IP_FORWARD
+ /* forwarding */
+ *sint_ptr = 1;
+#else
+ /* not-forwarding */
+ *sint_ptr = 2;
+#endif
+ }
+ break;
+ case 2: /* ipDefaultTTL */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = IP_DEFAULT_TTL;
+ }
+ break;
+ case 3: /* ipInReceives */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinreceives;
+ }
+ break;
+ case 4: /* ipInHdrErrors */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinhdrerrors;
+ }
+ break;
+ case 5: /* ipInAddrErrors */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinaddrerrors;
+ }
+ break;
+ case 6: /* ipForwDatagrams */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipforwdatagrams;
+ }
+ break;
+ case 7: /* ipInUnknownProtos */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipinunknownprotos;
+ }
+ break;
+ case 8: /* ipInDiscards */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipindiscards;
+ }
+ break;
+ case 9: /* ipInDelivers */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipindelivers;
+ }
+ break;
+ case 10: /* ipOutRequests */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipoutrequests;
+ }
+ break;
+ case 11: /* ipOutDiscards */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipoutdiscards;
+ }
+ break;
+ case 12: /* ipOutNoRoutes */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipoutnoroutes;
+ }
+ break;
+ case 13: /* ipReasmTimeout */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+#if IP_REASSEMBLY
+ *sint_ptr = IP_REASS_MAXAGE;
+#else
+ *sint_ptr = 0;
+#endif
+ }
+ break;
+ case 14: /* ipReasmReqds */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipreasmreqds;
+ }
+ break;
+ case 15: /* ipReasmOKs */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipreasmoks;
+ }
+ break;
+ case 16: /* ipReasmFails */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipreasmfails;
+ }
+ break;
+ case 17: /* ipFragOKs */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipfragoks;
+ }
+ break;
+ case 18: /* ipFragFails */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipfragfails;
+ }
+ break;
+ case 19: /* ipFragCreates */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = ipfragcreates;
+ }
+ break;
+ case 23: /* ipRoutingDiscards */
+ /** @todo can lwIP discard routes at all?? hardwire this to 0?? */
+ {
+ u32_t *uint_ptr = (u32_t*)value;
+ *uint_ptr = iproutingdiscards;
+ }
+ break;
+ };
+}
+
+/**
+ * Test ip object value before setting.
+ *
+ * @param od is the object definition
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value from.
+ *
+ * @note we allow set if the value matches the hardwired value,
+ * otherwise return badvalue.
+ */
+static u8_t
+ip_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id, set_ok;
+ s32_t *sint_ptr = (s32_t*)value;
+
+ LWIP_UNUSED_ARG(len);
+ set_ok = 0;
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipForwarding */
+#if IP_FORWARD
+ /* forwarding */
+ if (*sint_ptr == 1)
+#else
+ /* not-forwarding */
+ if (*sint_ptr == 2)
+#endif
+ {
+ set_ok = 1;
+ }
+ break;
+ case 2: /* ipDefaultTTL */
+ if (*sint_ptr == IP_DEFAULT_TTL)
+ {
+ set_ok = 1;
+ }
+ break;
+ };
+ return set_ok;
+}
+
+static void
+ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (4) */
+ ident_len += 4;
+ ident -= 4;
+
+ if (ident_len == 5)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipAdEntAddr */
+ case 3: /* ipAdEntNetMask */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 2: /* ipAdEntIfIndex */
+ case 4: /* ipAdEntBcastAddr */
+ case 5: /* ipAdEntReasmMaxSize */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+ u16_t ifidx;
+ ip_addr_t ip;
+ struct netif *netif = netif_list;
+
+ LWIP_UNUSED_ARG(len);
+ snmp_oidtoip(&od->id_inst_ptr[1], &ip);
+ ifidx = 0;
+ while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr))
+ {
+ netif = netif->next;
+ ifidx++;
+ }
+
+ if (netif != NULL)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipAdEntAddr */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+ *dst = netif->ip_addr;
+ }
+ break;
+ case 2: /* ipAdEntIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = ifidx + 1;
+ }
+ break;
+ case 3: /* ipAdEntNetMask */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+ *dst = netif->netmask;
+ }
+ break;
+ case 4: /* ipAdEntBcastAddr */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ /* lwIP oddity, there's no broadcast
+ address in the netif we can rely on */
+ *sint_ptr = IPADDR_BROADCAST & 1;
+ }
+ break;
+ case 5: /* ipAdEntReasmMaxSize */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+#if IP_REASSEMBLY
+ /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
+ * but only if receiving one fragmented packet at a time.
+ * The current solution is to calculate for 2 simultaneous packets...
+ */
+ *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) *
+ (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN)));
+#else
+ /** @todo returning MTU would be a bad thing and
+ returning a wild guess like '576' isn't good either */
+ *sint_ptr = 0;
+#endif
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * @note
+ * lwIP IP routing is currently using the network addresses in netif_list.
+ * if no suitable network IP is found in netif_list, the default_netif is used.
+ */
+static void
+ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (4) */
+ ident_len += 4;
+ ident -= 4;
+
+ if (ident_len == 5)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipRouteDest */
+ case 7: /* ipRouteNextHop */
+ case 11: /* ipRouteMask */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 2: /* ipRouteIfIndex */
+ case 3: /* ipRouteMetric1 */
+ case 4: /* ipRouteMetric2 */
+ case 5: /* ipRouteMetric3 */
+ case 6: /* ipRouteMetric4 */
+ case 8: /* ipRouteType */
+ case 10: /* ipRouteAge */
+ case 12: /* ipRouteMetric5 */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 9: /* ipRouteProto */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 13: /* ipRouteInfo */
+ /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
+ od->v_len = iprouteinfo.len * sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ struct netif *netif;
+ ip_addr_t dest;
+ s32_t *ident;
+ u8_t id;
+
+ ident = od->id_inst_ptr;
+ snmp_oidtoip(&ident[1], &dest);
+
+ if (ip_addr_isany(&dest))
+ {
+ /* ip_route() uses default netif for default route */
+ netif = netif_default;
+ }
+ else
+ {
+ /* not using ip_route(), need exact match! */
+ netif = netif_list;
+ while ((netif != NULL) &&
+ !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) )
+ {
+ netif = netif->next;
+ }
+ }
+ if (netif != NULL)
+ {
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipRouteDest */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte has 0.0.0.0 dest */
+ ip_addr_set_zero(dst);
+ }
+ else
+ {
+ /* netifs have netaddress dest */
+ ip_addr_get_network(dst, &netif->ip_addr, &netif->netmask);
+ }
+ }
+ break;
+ case 2: /* ipRouteIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ snmp_netiftoifindex(netif, sint_ptr);
+ }
+ break;
+ case 3: /* ipRouteMetric1 */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte has metric 1 */
+ *sint_ptr = 1;
+ }
+ else
+ {
+ /* other rtes have metric 0 */
+ *sint_ptr = 0;
+ }
+ }
+ break;
+ case 4: /* ipRouteMetric2 */
+ case 5: /* ipRouteMetric3 */
+ case 6: /* ipRouteMetric4 */
+ case 12: /* ipRouteMetric5 */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /* not used */
+ *sint_ptr = -1;
+ }
+ break;
+ case 7: /* ipRouteNextHop */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte: gateway */
+ *dst = netif->gw;
+ }
+ else
+ {
+ /* other rtes: netif ip_addr */
+ *dst = netif->ip_addr;
+ }
+ }
+ break;
+ case 8: /* ipRouteType */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte is indirect */
+ *sint_ptr = 4;
+ }
+ else
+ {
+ /* other rtes are direct */
+ *sint_ptr = 3;
+ }
+ }
+ break;
+ case 9: /* ipRouteProto */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /* locally defined routes */
+ *sint_ptr = 2;
+ }
+ break;
+ case 10: /* ipRouteAge */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /** @todo (sysuptime - timestamp last change) / 100
+ @see snmp_insert_iprteidx_tree() */
+ *sint_ptr = 0;
+ }
+ break;
+ case 11: /* ipRouteMask */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ if (ip_addr_isany(&dest))
+ {
+ /* default rte use 0.0.0.0 mask */
+ ip_addr_set_zero(dst);
+ }
+ else
+ {
+ /* other rtes use netmask */
+ *dst = netif->netmask;
+ }
+ }
+ break;
+ case 13: /* ipRouteInfo */
+ objectidncpy((s32_t*)value, (s32_t*)iprouteinfo.id, (u8_t)(len / sizeof(s32_t)));
+ break;
+ }
+ }
+}
+
+static void
+ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (5) */
+ ident_len += 5;
+ ident -= 5;
+
+ if (ident_len == 6)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* ipNetToMediaIfIndex */
+ case 4: /* ipNetToMediaType */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* ipNetToMediaPhysAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
+ od->v_len = 6; /** @todo try to use netif::hwaddr_len */
+ break;
+ case 3: /* ipNetToMediaNetAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+#if LWIP_ARP
+ u8_t id;
+ struct eth_addr* ethaddr_ret;
+ ip_addr_t* ipaddr_ret;
+#endif /* LWIP_ARP */
+ ip_addr_t ip;
+ struct netif *netif;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */
+
+ snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
+ snmp_oidtoip(&od->id_inst_ptr[2], &ip);
+
+#if LWIP_ARP /** @todo implement a netif_find_addr */
+ if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* ipNetToMediaIfIndex */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = od->id_inst_ptr[1];
+ }
+ break;
+ case 2: /* ipNetToMediaPhysAddress */
+ {
+ struct eth_addr *dst = (struct eth_addr*)value;
+
+ *dst = *ethaddr_ret;
+ }
+ break;
+ case 3: /* ipNetToMediaNetAddress */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+
+ *dst = *ipaddr_ret;
+ }
+ break;
+ case 4: /* ipNetToMediaType */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ /* dynamic (?) */
+ *sint_ptr = 3;
+ }
+ break;
+ }
+ }
+#endif /* LWIP_ARP */
+}
+
+static void
+icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if ((ident_len == 2) &&
+ (ident[0] > 0) && (ident[0] < 27))
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+icmp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* icmpInMsgs */
+ *uint_ptr = icmpinmsgs;
+ break;
+ case 2: /* icmpInErrors */
+ *uint_ptr = icmpinerrors;
+ break;
+ case 3: /* icmpInDestUnreachs */
+ *uint_ptr = icmpindestunreachs;
+ break;
+ case 4: /* icmpInTimeExcds */
+ *uint_ptr = icmpintimeexcds;
+ break;
+ case 5: /* icmpInParmProbs */
+ *uint_ptr = icmpinparmprobs;
+ break;
+ case 6: /* icmpInSrcQuenchs */
+ *uint_ptr = icmpinsrcquenchs;
+ break;
+ case 7: /* icmpInRedirects */
+ *uint_ptr = icmpinredirects;
+ break;
+ case 8: /* icmpInEchos */
+ *uint_ptr = icmpinechos;
+ break;
+ case 9: /* icmpInEchoReps */
+ *uint_ptr = icmpinechoreps;
+ break;
+ case 10: /* icmpInTimestamps */
+ *uint_ptr = icmpintimestamps;
+ break;
+ case 11: /* icmpInTimestampReps */
+ *uint_ptr = icmpintimestampreps;
+ break;
+ case 12: /* icmpInAddrMasks */
+ *uint_ptr = icmpinaddrmasks;
+ break;
+ case 13: /* icmpInAddrMaskReps */
+ *uint_ptr = icmpinaddrmaskreps;
+ break;
+ case 14: /* icmpOutMsgs */
+ *uint_ptr = icmpoutmsgs;
+ break;
+ case 15: /* icmpOutErrors */
+ *uint_ptr = icmpouterrors;
+ break;
+ case 16: /* icmpOutDestUnreachs */
+ *uint_ptr = icmpoutdestunreachs;
+ break;
+ case 17: /* icmpOutTimeExcds */
+ *uint_ptr = icmpouttimeexcds;
+ break;
+ case 18: /* icmpOutParmProbs */
+ *uint_ptr = icmpoutparmprobs;
+ break;
+ case 19: /* icmpOutSrcQuenchs */
+ *uint_ptr = icmpoutsrcquenchs;
+ break;
+ case 20: /* icmpOutRedirects */
+ *uint_ptr = icmpoutredirects;
+ break;
+ case 21: /* icmpOutEchos */
+ *uint_ptr = icmpoutechos;
+ break;
+ case 22: /* icmpOutEchoReps */
+ *uint_ptr = icmpoutechoreps;
+ break;
+ case 23: /* icmpOutTimestamps */
+ *uint_ptr = icmpouttimestamps;
+ break;
+ case 24: /* icmpOutTimestampReps */
+ *uint_ptr = icmpouttimestampreps;
+ break;
+ case 25: /* icmpOutAddrMasks */
+ *uint_ptr = icmpoutaddrmasks;
+ break;
+ case 26: /* icmpOutAddrMaskReps */
+ *uint_ptr = icmpoutaddrmaskreps;
+ break;
+ }
+}
+
+#if LWIP_TCP
+/** @todo tcp grp */
+static void
+tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ u8_t id;
+
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id));
+
+ switch (id)
+ {
+ case 1: /* tcpRtoAlgorithm */
+ case 2: /* tcpRtoMin */
+ case 3: /* tcpRtoMax */
+ case 4: /* tcpMaxConn */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 5: /* tcpActiveOpens */
+ case 6: /* tcpPassiveOpens */
+ case 7: /* tcpAttemptFails */
+ case 8: /* tcpEstabResets */
+ case 10: /* tcpInSegs */
+ case 11: /* tcpOutSegs */
+ case 12: /* tcpRetransSegs */
+ case 14: /* tcpInErrs */
+ case 15: /* tcpOutRsts */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 9: /* tcpCurrEstab */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE);
+ od->v_len = sizeof(u32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+tcp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ s32_t *sint_ptr = (s32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* tcpRtoAlgorithm, vanj(4) */
+ *sint_ptr = 4;
+ break;
+ case 2: /* tcpRtoMin */
+ /* @todo not the actual value, a guess,
+ needs to be calculated */
+ *sint_ptr = 1000;
+ break;
+ case 3: /* tcpRtoMax */
+ /* @todo not the actual value, a guess,
+ needs to be calculated */
+ *sint_ptr = 60000;
+ break;
+ case 4: /* tcpMaxConn */
+ *sint_ptr = MEMP_NUM_TCP_PCB;
+ break;
+ case 5: /* tcpActiveOpens */
+ *uint_ptr = tcpactiveopens;
+ break;
+ case 6: /* tcpPassiveOpens */
+ *uint_ptr = tcppassiveopens;
+ break;
+ case 7: /* tcpAttemptFails */
+ *uint_ptr = tcpattemptfails;
+ break;
+ case 8: /* tcpEstabResets */
+ *uint_ptr = tcpestabresets;
+ break;
+ case 9: /* tcpCurrEstab */
+ {
+ u16_t tcpcurrestab = 0;
+ struct tcp_pcb *pcb = tcp_active_pcbs;
+ while (pcb != NULL)
+ {
+ if ((pcb->state == ESTABLISHED) ||
+ (pcb->state == CLOSE_WAIT))
+ {
+ tcpcurrestab++;
+ }
+ pcb = pcb->next;
+ }
+ *uint_ptr = tcpcurrestab;
+ }
+ break;
+ case 10: /* tcpInSegs */
+ *uint_ptr = tcpinsegs;
+ break;
+ case 11: /* tcpOutSegs */
+ *uint_ptr = tcpoutsegs;
+ break;
+ case 12: /* tcpRetransSegs */
+ *uint_ptr = tcpretranssegs;
+ break;
+ case 14: /* tcpInErrs */
+ *uint_ptr = tcpinerrs;
+ break;
+ case 15: /* tcpOutRsts */
+ *uint_ptr = tcpoutrsts;
+ break;
+ }
+}
+#ifdef THIS_SEEMS_UNUSED
+static void
+tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (10) */
+ ident_len += 10;
+ ident -= 10;
+
+ if (ident_len == 11)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ id = ident[0];
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id));
+
+ switch (id)
+ {
+ case 1: /* tcpConnState */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ case 2: /* tcpConnLocalAddress */
+ case 4: /* tcpConnRemAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 3: /* tcpConnLocalPort */
+ case 5: /* tcpConnRemPort */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ ip_addr_t lip, rip;
+ u16_t lport, rport;
+ s32_t *ident;
+
+ ident = od->id_inst_ptr;
+ snmp_oidtoip(&ident[1], &lip);
+ lport = ident[5];
+ snmp_oidtoip(&ident[6], &rip);
+ rport = ident[10];
+
+ /** @todo find matching PCB */
+}
+#endif /* if 0 */
+#endif
+
+static void
+udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if ((ident_len == 2) &&
+ (ident[0] > 0) && (ident[0] < 6))
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+udp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* udpInDatagrams */
+ *uint_ptr = udpindatagrams;
+ break;
+ case 2: /* udpNoPorts */
+ *uint_ptr = udpnoports;
+ break;
+ case 3: /* udpInErrors */
+ *uint_ptr = udpinerrors;
+ break;
+ case 4: /* udpOutDatagrams */
+ *uint_ptr = udpoutdatagrams;
+ break;
+ }
+}
+
+static void
+udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (5) */
+ ident_len += 5;
+ ident -= 5;
+
+ if (ident_len == 6)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ switch (ident[0])
+ {
+ case 1: /* udpLocalAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 2: /* udpLocalPort */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+udpentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+ struct udp_pcb *pcb;
+ ip_addr_t ip;
+ u16_t port;
+
+ LWIP_UNUSED_ARG(len);
+ snmp_oidtoip(&od->id_inst_ptr[1], &ip);
+ LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff));
+ port = (u16_t)od->id_inst_ptr[5];
+
+ pcb = udp_pcbs;
+ while ((pcb != NULL) &&
+ !(ip_addr_cmp(&pcb->local_ip, &ip) &&
+ (pcb->local_port == port)))
+ {
+ pcb = pcb->next;
+ }
+
+ if (pcb != NULL)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* udpLocalAddress */
+ {
+ ip_addr_t *dst = (ip_addr_t*)value;
+ *dst = pcb->local_ip;
+ }
+ break;
+ case 2: /* udpLocalPort */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = pcb->local_port;
+ }
+ break;
+ }
+ }
+}
+
+static void
+snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (1) */
+ ident_len += 1;
+ ident -= 1;
+ if (ident_len == 2)
+ {
+ u8_t id;
+
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
+ id = (u8_t)ident[0];
+ switch (id)
+ {
+ case 1: /* snmpInPkts */
+ case 2: /* snmpOutPkts */
+ case 3: /* snmpInBadVersions */
+ case 4: /* snmpInBadCommunityNames */
+ case 5: /* snmpInBadCommunityUses */
+ case 6: /* snmpInASNParseErrs */
+ case 8: /* snmpInTooBigs */
+ case 9: /* snmpInNoSuchNames */
+ case 10: /* snmpInBadValues */
+ case 11: /* snmpInReadOnlys */
+ case 12: /* snmpInGenErrs */
+ case 13: /* snmpInTotalReqVars */
+ case 14: /* snmpInTotalSetVars */
+ case 15: /* snmpInGetRequests */
+ case 16: /* snmpInGetNexts */
+ case 17: /* snmpInSetRequests */
+ case 18: /* snmpInGetResponses */
+ case 19: /* snmpInTraps */
+ case 20: /* snmpOutTooBigs */
+ case 21: /* snmpOutNoSuchNames */
+ case 22: /* snmpOutBadValues */
+ case 24: /* snmpOutGenErrs */
+ case 25: /* snmpOutGetRequests */
+ case 26: /* snmpOutGetNexts */
+ case 27: /* snmpOutSetRequests */
+ case 28: /* snmpOutGetResponses */
+ case 29: /* snmpOutTraps */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
+ od->v_len = sizeof(u32_t);
+ break;
+ case 30: /* snmpEnableAuthenTraps */
+ od->instance = MIB_OBJECT_SCALAR;
+ od->access = MIB_OBJECT_READ_WRITE;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ };
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+snmp_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* snmpInPkts */
+ *uint_ptr = snmpinpkts;
+ break;
+ case 2: /* snmpOutPkts */
+ *uint_ptr = snmpoutpkts;
+ break;
+ case 3: /* snmpInBadVersions */
+ *uint_ptr = snmpinbadversions;
+ break;
+ case 4: /* snmpInBadCommunityNames */
+ *uint_ptr = snmpinbadcommunitynames;
+ break;
+ case 5: /* snmpInBadCommunityUses */
+ *uint_ptr = snmpinbadcommunityuses;
+ break;
+ case 6: /* snmpInASNParseErrs */
+ *uint_ptr = snmpinasnparseerrs;
+ break;
+ case 8: /* snmpInTooBigs */
+ *uint_ptr = snmpintoobigs;
+ break;
+ case 9: /* snmpInNoSuchNames */
+ *uint_ptr = snmpinnosuchnames;
+ break;
+ case 10: /* snmpInBadValues */
+ *uint_ptr = snmpinbadvalues;
+ break;
+ case 11: /* snmpInReadOnlys */
+ *uint_ptr = snmpinreadonlys;
+ break;
+ case 12: /* snmpInGenErrs */
+ *uint_ptr = snmpingenerrs;
+ break;
+ case 13: /* snmpInTotalReqVars */
+ *uint_ptr = snmpintotalreqvars;
+ break;
+ case 14: /* snmpInTotalSetVars */
+ *uint_ptr = snmpintotalsetvars;
+ break;
+ case 15: /* snmpInGetRequests */
+ *uint_ptr = snmpingetrequests;
+ break;
+ case 16: /* snmpInGetNexts */
+ *uint_ptr = snmpingetnexts;
+ break;
+ case 17: /* snmpInSetRequests */
+ *uint_ptr = snmpinsetrequests;
+ break;
+ case 18: /* snmpInGetResponses */
+ *uint_ptr = snmpingetresponses;
+ break;
+ case 19: /* snmpInTraps */
+ *uint_ptr = snmpintraps;
+ break;
+ case 20: /* snmpOutTooBigs */
+ *uint_ptr = snmpouttoobigs;
+ break;
+ case 21: /* snmpOutNoSuchNames */
+ *uint_ptr = snmpoutnosuchnames;
+ break;
+ case 22: /* snmpOutBadValues */
+ *uint_ptr = snmpoutbadvalues;
+ break;
+ case 24: /* snmpOutGenErrs */
+ *uint_ptr = snmpoutgenerrs;
+ break;
+ case 25: /* snmpOutGetRequests */
+ *uint_ptr = snmpoutgetrequests;
+ break;
+ case 26: /* snmpOutGetNexts */
+ *uint_ptr = snmpoutgetnexts;
+ break;
+ case 27: /* snmpOutSetRequests */
+ *uint_ptr = snmpoutsetrequests;
+ break;
+ case 28: /* snmpOutGetResponses */
+ *uint_ptr = snmpoutgetresponses;
+ break;
+ case 29: /* snmpOutTraps */
+ *uint_ptr = snmpouttraps;
+ break;
+ case 30: /* snmpEnableAuthenTraps */
+ *uint_ptr = *snmpenableauthentraps_ptr;
+ break;
+ };
+}
+
+/**
+ * Test snmp object value before setting.
+ *
+ * @param od is the object definition
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value from.
+ */
+static u8_t
+snmp_set_test(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id, set_ok;
+
+ LWIP_UNUSED_ARG(len);
+ set_ok = 0;
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ if (id == 30)
+ {
+ /* snmpEnableAuthenTraps */
+ s32_t *sint_ptr = (s32_t*)value;
+
+ if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default)
+ {
+ /* we should have writable non-volatile mem here */
+ if ((*sint_ptr == 1) || (*sint_ptr == 2))
+ {
+ set_ok = 1;
+ }
+ }
+ else
+ {
+ /* const or hardwired value */
+ if (*sint_ptr == snmpenableauthentraps_default)
+ {
+ set_ok = 1;
+ }
+ }
+ }
+ return set_ok;
+}
+
+static void
+snmp_set_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+
+ LWIP_UNUSED_ARG(len);
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ if (id == 30)
+ {
+ /* snmpEnableAuthenTraps */
+ /* @todo @fixme: which kind of pointer is 'value'? s32_t or u8_t??? */
+ u8_t *ptr = (u8_t*)value;
+ *snmpenableauthentraps_ptr = *ptr;
+ }
+}
+
+#endif /* LWIP_SNMP */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lwip/core/snmp/mib_structs.c Thu Sep 08 10:48:09 2011 +0000
@@ -0,0 +1,1174 @@
+/**
+ * @file
+ * MIB tree access/construction functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_structs.h"
+#include "lwip/memp.h"
+#include "lwip/netif.h"
+
+/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */
+const s32_t prefix[4] = {1, 3, 6, 1};
+
+#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN)
+/** node stack entry (old news?) */
+struct nse
+{
+ /** right child */
+ struct mib_node* r_ptr;
+ /** right child identifier */
+ s32_t r_id;
+ /** right child next level */
+ u8_t r_nl;
+};
+static u8_t node_stack_cnt;
+static struct nse node_stack[NODE_STACK_SIZE];
+
+/**
+ * Pushes nse struct onto stack.
+ */
+static void
+push_node(struct nse* node)
+{
+ LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id));
+ if (node_stack_cnt < NODE_STACK_SIZE)
+ {
+ node_stack[node_stack_cnt] = *node;
+ node_stack_cnt++;
+ }
+}
+
+/**
+ * Pops nse struct from stack.
+ */
+static void
+pop_node(struct nse* node)
+{
+ if (node_stack_cnt > 0)
+ {
+ node_stack_cnt--;
+ *node = node_stack[node_stack_cnt];
+ }
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id));
+}
+
+/**
+ * Conversion from ifIndex to lwIP netif
+ * @param ifindex is a s32_t object sub-identifier
+ * @param netif points to returned netif struct pointer
+ */
+void
+snmp_ifindextonetif(s32_t ifindex, struct netif **netif)
+{
+ struct netif *nif = netif_list;
+ s32_t i, ifidx;
+
+ ifidx = ifindex - 1;
+ i = 0;
+ while ((nif != NULL) && (i < ifidx))
+ {
+ nif = nif->next;
+ i++;
+ }
+ *netif = nif;
+}
+
+/**
+ * Conversion from lwIP netif to ifIndex
+ * @param netif points to a netif struct
+ * @param ifidx points to s32_t object sub-identifier
+ */
+void
+snmp_netiftoifindex(struct netif *netif, s32_t *ifidx)
+{
+ struct netif *nif = netif_list;
+ u16_t i;
+
+ i = 0;
+ while ((nif != NULL) && (nif != netif))
+ {
+ nif = nif->next;
+ i++;
+ }
+ *ifidx = i+1;
+}
+
+/**
+ * Conversion from oid to lwIP ip_addr
+ * @param ident points to s32_t ident[4] input
+ * @param ip points to output struct
+ */
+void
+snmp_oidtoip(s32_t *ident, ip_addr_t *ip)
+{
+ IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]);
+}
+
+/**
+ * Conversion from lwIP ip_addr to oid
+ * @param ip points to input struct
+ * @param ident points to s32_t ident[4] output
+ */
+void
+snmp_iptooid(ip_addr_t *ip, s32_t *ident)
+{
+ ident[0] = ip4_addr1(ip);
+ ident[1] = ip4_addr2(ip);
+ ident[2] = ip4_addr3(ip);
+ ident[3] = ip4_addr4(ip);
+}
+
+struct mib_list_node *
+snmp_mib_ln_alloc(s32_t id)
+{
+ struct mib_list_node *ln;
+
+ ln = (struct mib_list_node *)memp_malloc(MEMP_SNMP_NODE);
+ if (ln != NULL)
+ {
+ ln->prev = NULL;
+ ln->next = NULL;
+ ln->objid = id;
+ ln->nptr = NULL;
+ }
+ return ln;
+}
+
+void
+snmp_mib_ln_free(struct mib_list_node *ln)
+{
+ memp_free(MEMP_SNMP_NODE, ln);
+}
+
+struct mib_list_rootnode *
+snmp_mib_lrn_alloc(void)
+{
+ struct mib_list_rootnode *lrn;
+
+ lrn = (struct mib_list_rootnode*)memp_malloc(MEMP_SNMP_ROOTNODE);
+ if (lrn != NULL)
+ {
+ lrn->get_object_def = noleafs_get_object_def;
+ lrn->get_value = noleafs_get_value;
+ lrn->set_test = noleafs_set_test;
+ lrn->set_value = noleafs_set_value;
+ lrn->node_type = MIB_NODE_LR;
+ lrn->maxlength = 0;
+ lrn->head = NULL;
+ lrn->tail = NULL;
+ lrn->count = 0;
+ }
+ return lrn;
+}
+
+void
+snmp_mib_lrn_free(struct mib_list_rootnode *lrn)
+{
+ memp_free(MEMP_SNMP_ROOTNODE, lrn);
+}
+
+/**
+ * Inserts node in idx list in a sorted
+ * (ascending order) fashion and
+ * allocates the node if needed.
+ *
+ * @param rn points to the root node
+ * @param objid is the object sub identifier
+ * @param insn points to a pointer to the inserted node
+ * used for constructing the tree.
+ * @return -1 if failed, 1 if inserted, 2 if present.
+ */
+s8_t
+snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn)
+{
+ struct mib_list_node *nn;
+ s8_t insert;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+
+ /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */
+ insert = 0;
+ if (rn->head == NULL)
+ {
+ /* empty list, add first node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ rn->head = nn;
+ rn->tail = nn;
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ insert = -1;
+ }
+ }
+ else
+ {
+ struct mib_list_node *n;
+ /* at least one node is present */
+ n = rn->head;
+ while ((n != NULL) && (insert == 0))
+ {
+ if (n->objid == objid)
+ {
+ /* node is already there */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid));
+ *insn = n;
+ insert = 2;
+ }
+ else if (n->objid < objid)
+ {
+ if (n->next == NULL)
+ {
+ /* alloc and insert at the tail */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ nn->next = NULL;
+ nn->prev = n;
+ n->next = nn;
+ rn->tail = nn;
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ /* insertion failure */
+ insert = -1;
+ }
+ }
+ else
+ {
+ /* there's more to explore: traverse list */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n"));
+ n = n->next;
+ }
+ }
+ else
+ {
+ /* n->objid > objid */
+ /* alloc and insert between n->prev and n */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ if (n->prev == NULL)
+ {
+ /* insert at the head */
+ nn->next = n;
+ nn->prev = NULL;
+ rn->head = nn;
+ n->prev = nn;
+ }
+ else
+ {
+ /* insert in the middle */
+ nn->next = n;
+ nn->prev = n->prev;
+ n->prev->next = nn;
+ n->prev = nn;
+ }
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ /* insertion failure */
+ insert = -1;
+ }
+ }
+ }
+ }
+ if (insert == 1)
+ {
+ rn->count += 1;
+ }
+ LWIP_ASSERT("insert != 0",insert != 0);
+ return insert;
+}
+
+/**
+ * Finds node in idx list and returns deletion mark.
+ *
+ * @param rn points to the root node
+ * @param objid is the object sub identifier
+ * @param fn returns pointer to found node
+ * @return 0 if not found, 1 if deletable,
+ * 2 can't delete (2 or more children), 3 not a list_node
+ */
+s8_t
+snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn)
+{
+ s8_t fc;
+ struct mib_list_node *n;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+ n = rn->head;
+ while ((n != NULL) && (n->objid != objid))
+ {
+ n = n->next;
+ }
+ if (n == NULL)
+ {
+ fc = 0;
+ }
+ else if (n->nptr == NULL)
+ {
+ /* leaf, can delete node */
+ fc = 1;
+ }
+ else
+ {
+ struct mib_list_rootnode *r;
+
+ if (n->nptr->node_type == MIB_NODE_LR)
+ {
+ r = (struct mib_list_rootnode *)n->nptr;
+ if (r->count > 1)
+ {
+ /* can't delete node */
+ fc = 2;
+ }
+ else
+ {
+ /* count <= 1, can delete node */
+ fc = 1;
+ }
+ }
+ else
+ {
+ /* other node type */
+ fc = 3;
+ }
+ }
+ *fn = n;
+ return fc;
+}
+
+/**
+ * Removes node from idx list
+ * if it has a single child left.
+ *
+ * @param rn points to the root node
+ * @param n points to the node to delete
+ * @return the nptr to be freed by caller
+ */
+struct mib_list_rootnode *
+snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n)
+{
+ struct mib_list_rootnode *next;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+ LWIP_ASSERT("n != NULL",n != NULL);
+
+ /* caller must remove this sub-tree */
+ next = (struct mib_list_rootnode*)(n->nptr);
+ rn->count -= 1;
+
+ if (n == rn->head)
+ {
+ rn->head = n->next;
+ if (n->next != NULL)
+ {
+ /* not last node, new list begin */
+ n->next->prev = NULL;
+ }
+ }
+ else if (n == rn->tail)
+ {
+ rn->tail = n->prev;
+ if (n->prev != NULL)
+ {
+ /* not last node, new list end */
+ n->prev->next = NULL;
+ }
+ }
+ else
+ {
+ /* node must be in the middle */
+ n->prev->next = n->next;
+ n->next->prev = n->prev;
+ }
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid));
+ snmp_mib_ln_free(n);
+ if (rn->count == 0)
+ {
+ rn->head = NULL;
+ rn->tail = NULL;
+ }
+ return next;
+}
+
+
+
+/**
+ * Searches tree for the supplied (scalar?) object identifier.
+ *
+ * @param node points to the root of the tree ('.internet')
+ * @param ident_len the length of the supplied object identifier
+ * @param ident points to the array of sub identifiers
+ * @param np points to the found object instance (return)
+ * @return 