Webserver+3d print

Dependents:   Nucleo

Revision:
0:8918a71cdbe9
diff -r 000000000000 -r 8918a71cdbe9 cyclone_tcp/core/net.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/net.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1550 @@
+/**
+ * @file net.c
+ * @brief TCP/IP stack core
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL ETH_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "core/net.h"
+#include "core/socket.h"
+#include "core/tcp_timer.h"
+#include "core/ethernet.h"
+#include "ipv4/arp.h"
+#include "ipv4/ipv4.h"
+#include "ipv4/ipv4_routing.h"
+#include "ipv4/igmp.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_routing.h"
+#include "ipv6/mld.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_router_adv.h"
+#include "dhcp/dhcp_client.h"
+#include "dhcp/dhcp_server.h"
+#include "dns/dns_cache.h"
+#include "dns/dns_client.h"
+#include "mdns/mdns_client.h"
+#include "mdns/mdns_responder.h"
+#include "mdns/mdns_common.h"
+#include "dns_sd/dns_sd.h"
+#include "netbios/nbns_client.h"
+#include "netbios/nbns_responder.h"
+#include "netbios/nbns_common.h"
+#include "dns_sd/dns_sd.h"
+#include "mibs/mib2_module.h"
+#include "str.h"
+#include "debug.h"
+
+#if (WEB_SOCKET_SUPPORT == ENABLED)
+   #include "web_socket/web_socket.h"
+#endif
+
+//TCP/IP stack handle
+OsTask *netTaskHandle;
+//Mutex preventing simultaneous access to the TCP/IP stack
+OsMutex netMutex;
+//Event object to receive notifications from device drivers
+OsEvent netEvent;
+//Network interfaces
+NetInterface netInterface[NET_INTERFACE_COUNT];
+
+//TCP/IP process state
+static bool_t netTaskRunning;
+//Timestamp
+static systime_t netTimestamp;
+//Pseudo-random number generator state
+static uint32_t prngState = 0;
+
+//Mutex to prevent simultaneous access to the callback table
+static OsMutex callbackTableMutex;
+//Table that holds the registered user callbacks
+static LinkChangeCallbackDesc callbackTable[NET_CALLBACK_TABLE_SIZE];
+
+//Check TCP/IP stack configuration
+#if (NET_STATIC_OS_RESOURCES == ENABLED)
+
+//Task responsible for handling TCP/IP events
+static OsTask netTaskInstance;
+static uint_t netTaskStack[NET_TASK_STACK_SIZE];
+
+#endif
+
+
+/**
+ * @brief TCP/IP stack initialization
+ * @return Error code
+ **/
+
+error_t netInit(void)
+{
+   error_t error;
+   uint_t i;
+   NetInterface *interface;
+
+   //The TCP/IP process is currently suspended
+   netTaskRunning = FALSE;
+   //Get current time
+   netTimestamp = osGetSystemTime();
+
+   //Create a mutex to prevent simultaneous access to the TCP/IP stack
+   if(!osCreateMutex(&netMutex))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Create a event object to receive notifications from device drivers
+   if(!osCreateEvent(&netEvent))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Memory pool initialization
+   error = memPoolInit();
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Clear configuration data for each interface
+   memset(netInterface, 0, sizeof(netInterface));
+
+   //Save the number of interfaces
+   MIB2_SET_INTEGER(mib2Base.ifGroup.ifNumber, NET_INTERFACE_COUNT);
+
+   //Loop through network interfaces
+   for(i = 0; i < NET_INTERFACE_COUNT; i++)
+   {
+      //Point to the current interface
+      interface = &netInterface[i];
+
+      //Default interface name
+      sprintf(interface->name, "eth%u", i);
+      //Default interface identifier
+      interface->id = i;
+      //Default PHY address
+      interface->phyAddr = UINT8_MAX;
+
+#if (MIB2_SUPPORT == ENABLED)
+      //MIB ifEntry object
+      interface->mibIfEntry = &mib2Base.ifGroup.ifTable[i];
+#endif
+
+      //Interface identifier
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifIndex, i + 1);
+      //Interface name
+      MIB2_SET_OCTET_STRING(interface->mibIfEntry->ifDescr, interface->name, strlen(interface->name));
+      MIB2_SET_OCTET_STRING_LEN(interface->mibIfEntry->ifDescrLen, strlen(interface->name));
+      //Default interface type
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifType, MIB2_IF_TYPE_OTHER);
+      //Default interface state
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifAdminStatus, MIB2_IF_ADMIN_STATUS_DOWN);
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifOperStatus, MIB2_IF_OPER_STATUS_DOWN);
+   }
+
+   //Create a mutex to prevent simultaneous access to the callback table
+   if(!osCreateMutex(&callbackTableMutex))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Initialize callback table
+   memset(callbackTable, 0, sizeof(callbackTable));
+
+   //Socket related initialization
+   error = socketInit();
+   //Any error to report?
+   if(error)
+      return error;
+
+#if (WEB_SOCKET_SUPPORT == ENABLED)
+   //WebSocket related initialization
+   webSocketInit();
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && IPV4_ROUTING_SUPPORT == ENABLED)
+   //Initialize IPv4 routing table
+   error = ipv4InitRouting();
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && IPV6_ROUTING_SUPPORT == ENABLED)
+   //Initialize IPv6 routing table
+   error = ipv6InitRouting();
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+#if (UDP_SUPPORT == ENABLED)
+   //UDP related initialization
+   error = udpInit();
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+#if (TCP_SUPPORT == ENABLED)
+   //TCP related initialization
+   error = tcpInit();
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+#if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
+   NBNS_CLIENT_SUPPORT == ENABLED)
+   //DNS cache initialization
+   error = dnsInit();
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+   //Initialize tick counters
+   nicTickCounter = 0;
+
+#if (PPP_SUPPORT == ENABLED)
+   pppTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && ETH_SUPPORT == ENABLED)
+   arpTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && IPV4_FRAG_SUPPORT == ENABLED)
+   ipv4FragTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && IGMP_SUPPORT == ENABLED)
+   igmpTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && AUTO_IP_SUPPORT == ENABLED)
+   autoIpTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && DHCP_CLIENT_SUPPORT == ENABLED)
+   dhcpClientTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && DHCP_SERVER_SUPPORT == ENABLED)
+   dhcpServerTickCounter = 0;
+#endif
+#if (IPV6_SUPPORT == ENABLED && IPV6_FRAG_SUPPORT == ENABLED)
+   ipv6FragTickCounter = 0;
+#endif
+#if (IPV6_SUPPORT == ENABLED && MLD_SUPPORT == ENABLED)
+   mldTickCounter = 0;
+#endif
+#if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED)
+   ndpTickCounter = 0;
+#endif
+#if (IPV6_SUPPORT == ENABLED && NDP_ROUTER_ADV_SUPPORT == ENABLED)
+   ndpRouterAdvTickCounter = 0;
+#endif
+#if (IPV6_SUPPORT == ENABLED && DHCPV6_CLIENT_SUPPORT == ENABLED)
+   dhcpv6ClientTickCounter = 0;
+#endif
+#if (TCP_SUPPORT == ENABLED)
+   tcpTickCounter = 0;
+#endif
+#if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
+   NBNS_CLIENT_SUPPORT == ENABLED)
+   dnsTickCounter = 0;
+#endif
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+   mdnsResponderTickCounter = 0;
+#endif
+#if (DNS_SD_SUPPORT == ENABLED)
+   dnsSdTickCounter = 0;
+#endif
+
+#if (NET_STATIC_OS_RESOURCES == ENABLED)
+   //Create a task to handle TCP/IP events
+   osCreateStaticTask(&netTaskInstance, "TCP/IP Stack", (OsTaskCode) netTask,
+      NULL, netTaskStack, NET_TASK_STACK_SIZE, NET_TASK_PRIORITY);
+#else
+   //Create a task to handle TCP/IP events
+   netTaskHandle = osCreateTask("TCP/IP Stack", (OsTaskCode) netTask,
+      NULL, NET_TASK_STACK_SIZE, NET_TASK_PRIORITY);
+
+   //Unable to create the task?
+   if(netTaskHandle == OS_INVALID_HANDLE)
+      return ERROR_OUT_OF_RESOURCES;
+#endif
+
+#if (NET_RTOS_SUPPORT == DISABLED)
+   //The TCP/IP process is now running
+   netTaskRunning = TRUE;
+#endif
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set MAC address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] macAddr MAC address
+ * @return Error code
+ **/
+
+error_t netSetMacAddr(NetInterface *interface, const MacAddr *macAddr)
+{
+   //Check parameters
+   if(interface == NULL || macAddr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (ETH_SUPPORT == ENABLED)
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set MAC address
+   interface->macAddr = *macAddr;
+
+   //Generate the 64-bit interface identifier
+   macAddrToEui64(macAddr, &interface->eui64);
+
+   //Interface's physical address
+   MIB2_SET_OCTET_STRING(interface->mibIfEntry->ifPhysAddress, &interface->macAddr, 6);
+   MIB2_SET_OCTET_STRING_LEN(interface->mibIfEntry->ifPhysAddressLen, 6);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve MAC address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[out] macAddr MAC address
+ * @return Error code
+ **/
+
+error_t netGetMacAddr(NetInterface *interface, MacAddr *macAddr)
+{
+   //Check parameters
+   if(interface == NULL || macAddr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (ETH_SUPPORT == ENABLED)
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get MAC address
+   *macAddr = interface->macAddr;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set EUI-64 interface identifier
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] eui64 Interface identifier
+ * @return Error code
+ **/
+
+error_t netSetEui64(NetInterface *interface, const Eui64 *eui64)
+{
+   //Check parameters
+   if(interface == NULL || eui64 == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set interface identifier
+   interface->eui64 = *eui64;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve EUI-64 interface identifier
+ * @param[in] interface Pointer to the desired network interface
+ * @param[out] eui64 Interface identifier
+ * @return Error code
+ **/
+
+error_t netGetEui64(NetInterface *interface, Eui64 *eui64)
+{
+   //Check parameters
+   if(interface == NULL || eui64 == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get interface identifier
+   *eui64 = interface->eui64;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set interface identifier
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] id Unique number identifying the interface
+ * @return Error code
+ **/
+
+error_t netSetInterfaceId(NetInterface *interface, uint32_t id)
+{
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set interface identifier
+   interface->id = id;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set interface name
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] name NULL-terminated string that contains the interface name
+ * @return Error code
+ **/
+
+error_t netSetInterfaceName(NetInterface *interface, const char_t *name)
+{
+#if (MIB2_SUPPORT == ENABLED)
+   size_t n;
+#endif
+
+   //Check parameters
+   if(interface == NULL || name == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set interface name
+   strSafeCopy(interface->name, name, NET_MAX_IF_NAME_LEN);
+
+#if (MIB2_SUPPORT == ENABLED)
+   //Get the length of the string
+   n = strlen(interface->name);
+
+   //Text string containing information about the interface
+   MIB2_SET_OCTET_STRING(interface->mibIfEntry->ifDescr, interface->name, n);
+   MIB2_SET_OCTET_STRING_LEN(interface->mibIfEntry->ifDescrLen, n);
+#endif
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set host name
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] name NULL-terminated string that contains the host name
+ * @return Error code
+ **/
+
+error_t netSetHostname(NetInterface *interface, const char_t *name)
+{
+   //Check parameters
+   if(interface == NULL || name == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set host name
+   strSafeCopy(interface->hostname, name, NET_MAX_HOSTNAME_LEN);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set proxy server
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] name Proxy server name
+ * @param[in] port Proxy server port
+ * @return Error code
+ **/
+
+error_t netSetProxy(NetInterface *interface, const char_t *name, uint16_t port)
+{
+   //Check parameters
+   if(interface == NULL || name == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set proxy server name
+   strSafeCopy(interface->proxyName, name, NET_MAX_PROXY_NAME_LEN);
+   //Set proxy server port
+   interface->proxyPort = port;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set Ethernet MAC driver
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] driver Ethernet MAC driver
+ * @return Error code
+ **/
+
+error_t netSetDriver(NetInterface *interface, const NicDriver *driver)
+{
+   //Check parameters
+   if(interface == NULL || driver == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set Ethernet MAC driver
+   interface->nicDriver = driver;
+
+   //Set interface type
+   if(driver->type == NIC_TYPE_ETHERNET)
+   {
+      //Ethernet interface
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifType, MIB2_IF_TYPE_ETHERNET_CSMACD);
+   }
+   else if(driver->type == NIC_TYPE_PPP)
+   {
+      //PPP interface
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifType, MIB2_IF_TYPE_PPP);
+   }
+   else if(driver->type == NIC_TYPE_6LOWPAN)
+   {
+      //IEEE 802.15.4 WPAN interface
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifType, MIB2_IF_TYPE_IEEE_802_15_4);
+   }
+   else
+   {
+      //Unknown interface type
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifType, MIB2_IF_TYPE_OTHER);
+   }
+
+   //Set interface MTU
+   MIB2_SET_INTEGER(interface->mibIfEntry->ifMtu, driver->mtu);
+   //Update interface state
+   MIB2_SET_INTEGER(interface->mibIfEntry->ifAdminStatus, MIB2_IF_ADMIN_STATUS_UP);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set Ethernet PHY driver
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] driver Ethernet PHY driver (can be NULL for MAC + PHY controller)
+ * @return Error code
+ **/
+
+error_t netSetPhyDriver(NetInterface *interface, const PhyDriver *driver)
+{
+   //Check parameters
+   if(interface == NULL || driver == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set Ethernet PHY driver
+   interface->phyDriver = driver;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set Ethernet PHY address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] phyAddr PHY address
+ * @return Error code
+ **/
+
+error_t netSetPhyAddr(NetInterface *interface, uint8_t phyAddr)
+{
+   //Make sure the network interface is valid
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure the PHY address is valid
+   if(phyAddr >= 32)
+      return ERROR_OUT_OF_RANGE;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set PHY address
+   interface->phyAddr = phyAddr;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set SPI driver
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] driver Underlying SPI driver
+ * @return Error code
+ **/
+
+error_t netSetSpiDriver(NetInterface *interface, const SpiDriver *driver)
+{
+   //Check parameters
+   if(interface == NULL || driver == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set SPI driver
+   interface->spiDriver = driver;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set UART driver
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] driver Underlying UART driver
+ * @return Error code
+ **/
+
+error_t netSetUartDriver(NetInterface *interface, const UartDriver *driver)
+{
+   //Check parameters
+   if(interface == NULL || driver == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set UART driver
+   interface->uartDriver = driver;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set external interrupt line driver
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] driver Underlying SPI driver
+ * @return Error code
+ **/
+
+error_t netSetExtIntDriver(NetInterface *interface, const ExtIntDriver *driver)
+{
+   //Check parameters
+   if(interface == NULL || driver == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set external interrupt line driver
+   interface->extIntDriver = driver;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set link state (for virtual drivers only)
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] linkState Link state
+ * @return Error code
+ **/
+
+error_t netSetLinkState(NetInterface *interface, NicLinkState linkState)
+{
+   //Make sure the network interface is valid
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Link state changed?
+   if(linkState != interface->linkState)
+   {
+      //Update link state
+      interface->linkState = linkState;
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get link state
+ * @param[in] interface Pointer to the desired network interface
+ * @return Link state
+ **/
+
+bool_t netGetLinkState(NetInterface *interface)
+{
+   bool_t linkState;
+
+   //Make sure the network interface is valid
+   if(interface == NULL)
+      return FALSE;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Retrieve link state
+   linkState = interface->linkState;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return link state
+   return linkState;
+}
+
+
+/**
+ * @brief Configure network interface
+ * @param[in] interface Network interface to configure
+ * @return Error code
+ **/
+
+error_t netConfigInterface(NetInterface *interface)
+{
+   error_t error;
+
+   //Make sure the network interface is valid
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Disable hardware interrupts
+   interface->nicDriver->disableIrq(interface);
+
+   //Start of exception handling block
+   do
+   {
+      //Receive notifications when the transmitter is ready to send
+      if(!osCreateEvent(&interface->nicTxEvent))
+      {
+         //Failed to create event object
+         error = ERROR_OUT_OF_RESOURCES;
+         //Stop immediately
+         break;
+      }
+
+      //Network controller initialization
+      error = interface->nicDriver->init(interface);
+      //Any error to report?
+      if(error)
+         break;
+
+#if (ETH_SUPPORT == ENABLED)
+      //Ethernet related initialization
+      error = ethInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Interface's physical address
+      MIB2_SET_OCTET_STRING(interface->mibIfEntry->ifPhysAddress, &interface->macAddr, 6);
+      MIB2_SET_OCTET_STRING_LEN(interface->mibIfEntry->ifPhysAddressLen, 6);
+#endif
+
+#if (IPV4_SUPPORT == ENABLED)
+      //IPv4 initialization
+      error = ipv4Init(interface);
+      //Any error to report?
+      if(error)
+         break;
+
+#if (ETH_SUPPORT == ENABLED)
+      //ARP cache initialization
+      error = arpInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+#if (IGMP_SUPPORT == ENABLED)
+      //IGMP related initialization
+      error = igmpInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Join the all-systems group
+      error = ipv4JoinMulticastGroup(interface, IGMP_ALL_SYSTEMS_ADDR);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+#if (NBNS_CLIENT_SUPPORT == ENABLED || NBNS_RESPONDER_SUPPORT == ENABLED)
+      //NetBIOS Name Service related initialization
+      error = nbnsInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+      //IPv6 initialization
+      error = ipv6Init(interface);
+      //Any error to report?
+      if(error)
+         break;
+
+#if (NDP_SUPPORT == ENABLED)
+      //NDP related initialization
+      error = ndpInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+#if (MLD_SUPPORT == ENABLED)
+      //MLD related initialization
+      error = mldInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+      //Join the All-Nodes multicast address
+      error = ipv6JoinMulticastGroup(interface, &IPV6_LINK_LOCAL_ALL_NODES_ADDR);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+#if (MDNS_CLIENT_SUPPORT == ENABLED || MDNS_RESPONDER_SUPPORT == ENABLED)
+      //mDNS related initialization
+      error = mdnsInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+      //End of exception handling block
+   } while(0);
+
+   //Check status code
+   if(!error)
+   {
+      //The network interface is now fully configured
+      interface->configured = TRUE;
+
+      //Check whether the TCP/IP process is running
+      if(netTaskRunning)
+      {
+         //Interrupts can be safely enabled
+         interface->nicDriver->enableIrq(interface);
+      }
+   }
+   else
+   {
+      //Clean up side effects before returning
+      osDeleteEvent(&interface->nicTxEvent);
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief TCP/IP events handling
+ **/
+
+void netTask(void)
+{
+   uint_t i;
+   bool_t status;
+   systime_t time;
+   systime_t timeout;
+   NetInterface *interface;
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //The TCP/IP process is now running
+   netTaskRunning = TRUE;
+
+   //Loop through network interfaces
+   for(i = 0; i < NET_INTERFACE_COUNT; i++)
+   {
+      //Point to the current network interface
+      interface = &netInterface[i];
+
+      //Check whether the interface is fully configured
+      if(interface->configured)
+      {
+         //Interrupts can be safely enabled
+         interface->nicDriver->enableIrq(interface);
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Main loop
+   while(1)
+   {
+#endif
+      //Get current time
+      time = osGetSystemTime();
+
+      //Compute the maximum blocking time when waiting for an event
+      if(timeCompare(time, netTimestamp) < 0)
+         timeout = netTimestamp - time;
+      else
+         timeout = 0;
+
+      //Receive notifications when a frame has been received, or the
+      //link state of any network interfaces has changed
+      status = osWaitForEvent(&netEvent, timeout);
+
+      //Check whether the specified event is in signaled state
+      if(status)
+      {
+         //Get exclusive access
+         osAcquireMutex(&netMutex);
+
+         //Process events
+         for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         {
+            //Point to the current network interface
+            interface = &netInterface[i];
+
+            //Check whether a NIC event is pending
+            if(interface->nicEvent)
+            {
+               //Acknowledge the event by clearing the flag
+               interface->nicEvent = FALSE;
+
+               //Disable hardware interrupts
+               interface->nicDriver->disableIrq(interface);
+               //Handle NIC events
+               interface->nicDriver->eventHandler(interface);
+               //Re-enable hardware interrupts
+               interface->nicDriver->enableIrq(interface);
+            }
+
+            //Check whether a PHY event is pending
+            if(interface->phyEvent)
+            {
+               //Acknowledge the event by clearing the flag
+               interface->phyEvent = FALSE;
+
+               //Disable hardware interrupts
+               interface->nicDriver->disableIrq(interface);
+               //Handle PHY events
+               interface->phyDriver->eventHandler(interface);
+               //Re-enable hardware interrupts
+               interface->nicDriver->enableIrq(interface);
+            }
+         }
+
+         //Release exclusive access
+         osReleaseMutex(&netMutex);
+      }
+
+      //Check current time
+      if(timeCompare(time, netTimestamp) > 0)
+      {
+         //Get exclusive access
+         osAcquireMutex(&netMutex);
+         //Handle periodic operations
+         netTick();
+         //Release exclusive access
+         osReleaseMutex(&netMutex);
+
+         //Next event
+         netTimestamp = time + NET_TICK_INTERVAL;
+      }
+#if (NET_RTOS_SUPPORT == ENABLED)
+   }
+#endif
+}
+
+
+/**
+ * @brief Manage TCP/IP timers
+ **/
+
+void netTick(void)
+{
+   uint_t i;
+
+   //Increment tick counter
+   nicTickCounter += NET_TICK_INTERVAL;
+
+   //Handle periodic operations such as polling the link state
+   if(nicTickCounter >= NIC_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            nicTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      nicTickCounter = 0;
+   }
+
+#if (PPP_SUPPORT == ENABLED)
+   //Increment tick counter
+   pppTickCounter += NET_TICK_INTERVAL;
+
+   //Manage PPP related timers
+   if(pppTickCounter >= PPP_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            pppTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      pppTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && ETH_SUPPORT == ENABLED)
+   //Increment tick counter
+   arpTickCounter += NET_TICK_INTERVAL;
+
+   //Manage ARP cache
+   if(arpTickCounter >= ARP_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            arpTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      arpTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && IPV4_FRAG_SUPPORT == ENABLED)
+   //Increment tick counter
+   ipv4FragTickCounter += NET_TICK_INTERVAL;
+
+   //Handle IPv4 fragment reassembly timeout
+   if(ipv4FragTickCounter >= IPV4_FRAG_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            ipv4FragTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      ipv4FragTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && IGMP_SUPPORT == ENABLED)
+   //Increment tick counter
+   igmpTickCounter += NET_TICK_INTERVAL;
+
+   //Handle IGMP related timers
+   if(igmpTickCounter >= IGMP_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            igmpTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      igmpTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && AUTO_IP_SUPPORT == ENABLED)
+   //Increment tick counter
+   autoIpTickCounter += NET_TICK_INTERVAL;
+
+   //Handle Auto-IP related timers
+   if(autoIpTickCounter >= AUTO_IP_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         autoIpTick(netInterface[i].autoIpContext);
+
+      //Reset tick counter
+      autoIpTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && DHCP_CLIENT_SUPPORT == ENABLED)
+   //Increment tick counter
+   dhcpClientTickCounter += NET_TICK_INTERVAL;
+
+   //Handle DHCP client related timers
+   if(dhcpClientTickCounter >= DHCP_CLIENT_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         dhcpClientTick(netInterface[i].dhcpClientContext);
+
+      //Reset tick counter
+      dhcpClientTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && DHCP_SERVER_SUPPORT == ENABLED)
+   //Increment tick counter
+   dhcpServerTickCounter += NET_TICK_INTERVAL;
+
+   //Handle DHCP server related timers
+   if(dhcpServerTickCounter >= DHCP_SERVER_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         dhcpServerTick(netInterface[i].dhcpServerContext);
+
+      //Reset tick counter
+      dhcpServerTickCounter = 0;
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && IPV6_FRAG_SUPPORT == ENABLED)
+   //Increment tick counter
+   ipv6FragTickCounter += NET_TICK_INTERVAL;
+
+   //Handle IPv6 fragment reassembly timeout
+   if(ipv6FragTickCounter >= IPV6_FRAG_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            ipv6FragTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      ipv6FragTickCounter = 0;
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && MLD_SUPPORT == ENABLED)
+   //Increment tick counter
+   mldTickCounter += NET_TICK_INTERVAL;
+
+   //Handle MLD related timers
+   if(mldTickCounter >= MLD_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            mldTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      mldTickCounter = 0;
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED)
+   //Increment tick counter
+   ndpTickCounter += NET_TICK_INTERVAL;
+
+   //Handle NDP related timers
+   if(ndpTickCounter >= NDP_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            ndpTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      ndpTickCounter = 0;
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && NDP_ROUTER_ADV_SUPPORT == ENABLED)
+   //Increment tick counter
+   ndpRouterAdvTickCounter += NET_TICK_INTERVAL;
+
+   //Handle RA service related timers
+   if(ndpRouterAdvTickCounter >= NDP_ROUTER_ADV_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         ndpRouterAdvTick(netInterface[i].ndpRouterAdvContext);
+
+      //Reset tick counter
+      ndpRouterAdvTickCounter = 0;
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && DHCPV6_CLIENT_SUPPORT == ENABLED)
+   //Increment tick counter
+   dhcpv6ClientTickCounter += NET_TICK_INTERVAL;
+
+   //Handle DHCPv6 client related timers
+   if(dhcpv6ClientTickCounter >= DHCPV6_CLIENT_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         dhcpv6ClientTick(netInterface[i].dhcpv6ClientContext);
+
+      //Reset tick counter
+      dhcpv6ClientTickCounter = 0;
+   }
+#endif
+
+#if (TCP_SUPPORT == ENABLED)
+   //Increment tick counter
+   tcpTickCounter += NET_TICK_INTERVAL;
+
+   //Manage TCP related timers
+   if(tcpTickCounter >= TCP_TICK_INTERVAL)
+   {
+      //TCP timer handler
+      tcpTick();
+      //Reset tick counter
+      tcpTickCounter = 0;
+   }
+#endif
+
+#if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
+NBNS_CLIENT_SUPPORT == ENABLED)
+   //Increment tick counter
+   dnsTickCounter += NET_TICK_INTERVAL;
+
+   //Manage DNS cache
+   if(dnsTickCounter >= DNS_TICK_INTERVAL)
+   {
+      //DNS timer handler
+      dnsTick();
+      //Reset tick counter
+      dnsTickCounter = 0;
+   }
+#endif
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+   //Increment tick counter
+   mdnsResponderTickCounter += NET_TICK_INTERVAL;
+
+   //Manage mDNS probing and announcing
+   if(mdnsResponderTickCounter >= MDNS_RESPONDER_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         mdnsResponderTick(netInterface[i].mdnsResponderContext);
+
+      //Reset tick counter
+      mdnsResponderTickCounter = 0;
+   }
+#endif
+
+#if (DNS_SD_SUPPORT == ENABLED)
+   //Increment tick counter
+   dnsSdTickCounter += NET_TICK_INTERVAL;
+
+   //Manage DNS-SD probing and announcing
+   if(dnsSdTickCounter >= DNS_SD_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         dnsSdTick(netInterface[i].dnsSdContext);
+
+      //Reset tick counter
+      dnsSdTickCounter = 0;
+   }
+#endif
+}
+
+
+/**
+ * @brief Get default network interface
+ * @return Pointer to the default network interface to be used
+ **/
+
+NetInterface *netGetDefaultInterface(void)
+{
+   //Default network interface
+   return &netInterface[0];
+}
+
+
+/**
+ * @brief Seed pseudo-random number generator
+ * @param[in] seed An integer value to be used as seed by the pseudo-random number generator
+ * @return Error code
+ **/
+
+error_t netInitRand(uint32_t seed)
+{
+   //Seed the pseudo-random number generator
+   prngState += seed;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get a random value
+ * @return Error code
+ **/
+
+uint32_t netGetRand(void)
+{
+   uint32_t result;
+
+   //Use a linear congruential generator (LCG) to update the state of the PRNG
+   prngState *= 1103515245;
+   prngState += 12345;
+   result = (prngState >> 16) & 0x07FF;
+
+   prngState *= 1103515245;
+   prngState += 12345;
+   result <<= 10;
+   result |= (prngState >> 16) & 0x03FF;
+
+   prngState *= 1103515245;
+   prngState += 12345;
+   result <<= 10;
+   result |= (prngState >> 16) & 0x03FF;
+
+   //Return the resulting value
+   return result;
+}
+
+
+/**
+ * @brief Get a random value in the specified range
+ * @param[in] min Lower bound
+ * @param[in] max Upper bound
+ * @return Random value in the specified range
+ **/
+
+int32_t netGetRandRange(int32_t min, int32_t max)
+{
+   //Return a random value in the given range
+   return min + netGetRand() % (max - min + 1);
+}
+
+
+/**
+ * @brief Register link change callback
+ * @param[in] interface Underlying network interface
+ * @param[in] callback Callback function to be called when the link state changed
+ * @param[in] params Callback function parameter (optional)
+ * @param[out] cookie Identifier that can be used to unregister the callback function
+ * @return Error code
+ **/
+
+error_t netAttachLinkChangeCallback(NetInterface *interface,
+   LinkChangeCallback callback, void *params, uint_t *cookie)
+{
+   uint_t i;
+   LinkChangeCallbackDesc *entry;
+
+   //Acquire exclusive access to the callback table
+   osAcquireMutex(&callbackTableMutex);
+
+   //Loop through the table
+   for(i = 0; i < NET_CALLBACK_TABLE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &callbackTable[i];
+
+      //Check whether the entry is currently in used
+      if(entry->callback == NULL)
+      {
+         //Create a new entry
+         entry->interface = interface;
+         entry->callback = callback;
+         entry->params = params;
+         //We are done
+         break;
+      }
+   }
+
+   //Release exclusive access to the callback table
+   osReleaseMutex(&callbackTableMutex);
+
+   //Failed to attach the specified user callback?
+   if(i >= NET_CALLBACK_TABLE_SIZE)
+      return ERROR_OUT_OF_RESOURCES;
+
+   //Return a cookie that can be used later to unregister the callback
+   if(cookie != NULL)
+      *cookie = i;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Unregister link change callback
+ * @param[in] cookie Identifier specifying the callback to be unregistered
+ * @return Error code
+ **/
+
+error_t netDetachLinkChangeCallback(uint_t cookie)
+{
+   //Make sure the cookie is valid
+   if(cookie >= NET_CALLBACK_TABLE_SIZE)
+      return ERROR_INVALID_PARAMETER;
+
+   //Acquire exclusive access to the callback table
+   osAcquireMutex(&callbackTableMutex);
+   //Unregister user callback
+   callbackTable[cookie].callback = NULL;
+   //Release exclusive access to the callback table
+   osReleaseMutex(&callbackTableMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Invoke link change callback
+ * @param[in] interface Underlying network interface
+ * @param[in] linkState Link state
+ **/
+
+void netInvokeLinkChangeCallback(NetInterface *interface, bool_t linkState)
+{
+   uint_t i;
+   LinkChangeCallbackDesc *entry;
+
+   //Acquire exclusive access to the callback table
+   osAcquireMutex(&callbackTableMutex);
+
+   //Loop through the table
+   for(i = 0; i < NET_CALLBACK_TABLE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &callbackTable[i];
+
+      //Any registered callback?
+      if(entry->callback != NULL)
+      {
+         //Check whether the network interface matches the current entry
+         if(entry->interface == NULL || entry->interface == interface)
+         {
+            //Invoke user callback function
+            entry->callback(interface, linkState, entry->params);
+         }
+      }
+   }
+
+   //Release exclusive access to the callback table
+   osReleaseMutex(&callbackTableMutex);
+}
+