void lwip_network_uptime_completion(void *arg)
{
    NATIVE_PROFILE_PAL_NETWORK();

    struct netif* pNetIf = (struct netif*)arg;

    BOOL status = enc28j60_get_link_status(&g_ENC28J60_LWIP_Config.DeviceConfigs[0].SPI_Config);

    if(status != LwipNetworkStatus)
    {        
        if(status)
        {
            tcpip_callback((sys_timeout_handler)netif_set_link_up, (void*)pNetIf);
            tcpip_callback((sys_timeout_handler)netif_set_up, (void*)pNetIf);

            Network_PostEvent( NETWORK_EVENT_TYPE__AVAILABILITY_CHANGED, NETWORK_EVENT_FLAGS_IS_AVAILABLE );
        }
        else
        {
            tcpip_callback((sys_timeout_handler)netif_set_link_down, (void*)pNetIf);
            tcpip_callback((sys_timeout_handler)netif_set_down, (void*)pNetIf);

            Network_PostEvent( NETWORK_EVENT_TYPE__AVAILABILITY_CHANGED, 0);
        }

        Events_Set(SYSTEM_EVENT_FLAG_SOCKET);
        Events_Set(SYSTEM_EVENT_FLAG_NETWORK);

        LwipNetworkStatus = status;
    }

    LwipUpTimeCompletion.EnqueueDelta64( 2000000 );
}
void lwip_network_uptime_completion(void *arg)
{
    NATIVE_PROFILE_PAL_NETWORK();

    BOOL status = ENET_PHY_lwip_get_link_status( );

    if(status != LwipNetworkStatus)
    {
        struct netif* pNetIf = (struct netif*)arg;

        if(status)
        {
            SOCK_NetworkConfiguration *pNetCfg = &g_NetworkConfig.NetworkInterfaces[0];

            ENET_PHY_lwip_set_link_speed( );
            netif_set_up( pNetIf );

            if(pNetCfg->flags & SOCK_NETWORKCONFIGURATION_FLAGS_DHCP)
            {
                dhcp_start( pNetIf );
            }
            Network_PostEvent( NETWORK_EVENT_TYPE__AVAILABILITY_CHANGED, NETWORK_EVENT_FLAGS_IS_AVAILABLE );
        }
        else
        {
            netif_set_down( (struct netif*)arg );
            Network_PostEvent( NETWORK_EVENT_TYPE__AVAILABILITY_CHANGED, 0);
        }

        LwipNetworkStatus = status;
    }

    LwipUpTimeCompletion.EnqueueDelta64( 2000000 );
}
void lpc24xx_status_callback(struct netif *netif)
{
    if(LwipLastIpAddress != netif->ip_addr.addr)
    {
        Network_PostEvent( NETWORK_EVENT_TYPE_ADDRESS_CHANGED, 0 );
        LwipLastIpAddress = netif->ip_addr.addr;
    }
    
#if defined(_DEBUG)
    lcd_printf("\f\nLink Update: \n");
    lcd_printf("         IP: %d.%d.%d.%d\n", (netif->ip_addr.addr >>  0) & 0xFF, 
                                             (netif->ip_addr.addr >>  8) & 0xFF,
                                             (netif->ip_addr.addr >> 16) & 0xFF,
                                             (netif->ip_addr.addr >> 24) & 0xFF);
    lcd_printf("         GW: %d.%d.%d.%d\n", (netif->gw.addr >>  0) & 0xFF, 
                                             (netif->gw.addr >>  8) & 0xFF,
                                             (netif->gw.addr >> 16) & 0xFF,
                                             (netif->gw.addr >> 24) & 0xFF);

    debug_printf("\nLink Update: \r\n");
    debug_printf("         IP: %d.%d.%d.%d\r\n", (netif->ip_addr.addr >>  0) & 0xFF, 
                                             (netif->ip_addr.addr >>  8) & 0xFF,
                                             (netif->ip_addr.addr >> 16) & 0xFF,
                                             (netif->ip_addr.addr >> 24) & 0xFF);
    debug_printf("         GW: %d.%d.%d.%d\r\n", (netif->gw.addr >>  0) & 0xFF, 
                                             (netif->gw.addr >>  8) & 0xFF,
                                             (netif->gw.addr >> 16) & 0xFF,
                                             (netif->gw.addr >> 24) & 0xFF);
#endif
}
void AT91_EMAC__status_callback(struct netif *netif)
{
    if(LwipLastIpAddress != netif->ip_addr.addr)
    {
        Network_PostEvent( NETWORK_EVENT_TYPE_ADDRESS_CHANGED, 0 );
        LwipLastIpAddress = netif->ip_addr.addr;

        if(!LWIP_STATUS_isset(LWIP_STATUS_MultiCastInit))
        {
            LWIP_STATUS_set(LWIP_STATUS_MultiCastInit);
            Sockets_LWIP_Driver::InitializeMulticastDiscovery();
        }
   }

#if !defined(BUILD_RTM)
    lcd_printf("\f\n\n\n\n\n\nLink Update: \n");
    lcd_printf("         IP: %d.%d.%d.%d\n", (netif->ip_addr.addr >>  0) & 0xFF, 
                                             (netif->ip_addr.addr >>  8) & 0xFF,
                                             (netif->ip_addr.addr >> 16) & 0xFF,
                                             (netif->ip_addr.addr >> 24) & 0xFF);
    lcd_printf("         GW: %d.%d.%d.%d\n", (netif->gw.addr >>  0) & 0xFF, 
                                             (netif->gw.addr >>  8) & 0xFF,
                                             (netif->gw.addr >> 16) & 0xFF,
                                             (netif->gw.addr >> 24) & 0xFF);
    debug_printf("\nLink Update: \r\n");
    debug_printf("         IP: %d.%d.%d.%d\r\n", (netif->ip_addr.addr >>  0) & 0xFF, 
                                             (netif->ip_addr.addr >>  8) & 0xFF,
                                             (netif->ip_addr.addr >> 16) & 0xFF,
                                             (netif->ip_addr.addr >> 24) & 0xFF);
    debug_printf("         GW: %d.%d.%d.%d\r\n", (netif->gw.addr >>  0) & 0xFF, 
                                             (netif->gw.addr >>  8) & 0xFF,
                                             (netif->gw.addr >> 16) & 0xFF,
                                             (netif->gw.addr >> 24) & 0xFF);
#endif
}
int AT91_EMAC_LWIP_Driver::Open(int index)
{
    /* Network interface variables */
    struct ip_addr ipaddr, subnetmask, gateway;
    struct netif *pNetIF;
    int len;
    const SOCK_NetworkConfiguration *iface;

    /* Apply network configuration */
    iface = &g_NetworkConfig.NetworkInterfaces[index];

    len = g_AT91_EMAC_NetIF.hwaddr_len;
    if(len == 0 || iface->macAddressLen < len)
    {
        len = iface->macAddressLen;
        g_AT91_EMAC_NetIF.hwaddr_len = len;
    }
    memcpy(g_AT91_EMAC_NetIF.hwaddr, iface->macAddressBuffer, len);

    if(0 == (iface->flags & SOCK_NETWORKCONFIGURATION_FLAGS_DHCP))
    {
        ipaddr.addr     = iface->ipaddr;
        gateway.addr    = iface->gateway;
        subnetmask.addr = iface->subnetmask;
    }
    else
    {
        /* Set network address variables - this will be set by either DHCP or when the configuration is applied */
        IP4_ADDR(&gateway, 0,0,0,0);
        IP4_ADDR(&ipaddr, 0,0,0,0);
        IP4_ADDR(&subnetmask, 255,255,255,0);
    }

    // PHY Power Up
    CPU_GPIO_EnableOutputPin(g_AT91_EMAC_LWIP_Config.PHY_PD_GPIO_Pin, FALSE);

    // Enable Interrupt
    CPU_INTC_ActivateInterrupt(AT91C_ID_EMAC, (HAL_CALLBACK_FPN)AT91_EMAC_LWIP_interrupt, &g_AT91_EMAC_NetIF);

    g_AT91_EMAC_NetIF.flags = NETIF_FLAG_IGMP | NETIF_FLAG_BROADCAST;

    pNetIF = netif_add( &g_AT91_EMAC_NetIF, &ipaddr, &subnetmask, &gateway, NULL, AT91_EMAC_ethhw_init, ethernet_input );

    netif_set_default( pNetIF );

    LWIP_STATUS_setorclear( LWIP_STATUS_LinkUp, 0 != dm9161_lwip_GetLinkStatus( ) );

    if (LWIP_STATUS_isset(LWIP_STATUS_LinkUp))
    {
        netif_set_link_up( pNetIF );
        netif_set_up     ( pNetIF );

        Network_PostEvent( NETWORK_EVENT_TYPE__AVAILABILITY_CHANGED, NETWORK_EVENT_FLAGS_IS_AVAILABLE );
    }

    /* Initialize the continuation routine for the driver interrupt and receive */    
    InitContinuations( pNetIF );

    return g_AT91_EMAC_NetIF.num;
}
void lwip_network_uptime_completion(void *arg)
{
    NATIVE_PROFILE_PAL_NETWORK();

    BOOL status = dm9161_lwip_GetLinkStatus( ) != 0;

    if(!status)
    {
        status = dm9161_lwip_GetLinkStatus( ) != 0;
    }

    if(status != LWIP_STATUS_isset(LWIP_STATUS_LinkUp) )
    {
        struct netif* pNetIf = (struct netif*)arg;

        if(status)
        {
            tcpip_timeout(1000, (sys_timeout_handler)netif_set_link_up, (void*)pNetIf);
            tcpip_timeout(1000, (sys_timeout_handler)netif_set_up, (void*)pNetIf);

            Network_PostEvent( NETWORK_EVENT_TYPE__AVAILABILITY_CHANGED, NETWORK_EVENT_FLAGS_IS_AVAILABLE );
        }
        else
        {
            tcpip_callback((sys_timeout_handler)netif_set_link_down, (void*)pNetIf);
            tcpip_callback((sys_timeout_handler)netif_set_down, (void*)pNetIf);

            Network_PostEvent( NETWORK_EVENT_TYPE__AVAILABILITY_CHANGED, 0);
        }

        Events_Set(SYSTEM_EVENT_FLAG_SOCKET);
        Events_Set(SYSTEM_EVENT_FLAG_NETWORK);

        LWIP_STATUS_setorclear( LWIP_STATUS_LinkUp, status );
    }

    LwipUpTimeCompletion.EnqueueDelta64( 2000000 );
}
void enc28j60_status_callback(struct netif *netif)
{
    if(LwipLastIpAddress != netif->ip_addr.addr)
    {
        Network_PostEvent( NETWORK_EVENT_TYPE_ADDRESS_CHANGED, 0 );
        LwipLastIpAddress = netif->ip_addr.addr;
    }

#if !defined(BUILD_RTM)
    lcd_printf("\f\n\n\n\n\n\nLink Update: %s\n", (netif_is_up(netif) ? "UP  " : "DOWN") );
    lcd_printf("         IP: %d.%d.%d.%d\n", (netif->ip_addr.addr >>  0) & 0xFF, 
                                             (netif->ip_addr.addr >>  8) & 0xFF,
                                             (netif->ip_addr.addr >> 16) & 0xFF,
                                             (netif->ip_addr.addr >> 24) & 0xFF);
    lcd_printf("         SM: %d.%d.%d.%d\n", (netif->netmask.addr >>  0) & 0xFF, 
                                             (netif->netmask.addr >>  8) & 0xFF,
                                             (netif->netmask.addr >> 16) & 0xFF,
                                             (netif->netmask.addr >> 24) & 0xFF);    
    lcd_printf("         GW: %d.%d.%d.%d\n", (netif->gw.addr >>  0) & 0xFF, 
                                             (netif->gw.addr >>  8) & 0xFF,
                                             (netif->gw.addr >> 16) & 0xFF,
                                             (netif->gw.addr >> 24) & 0xFF);
    debug_printf("IP Address: %d.%d.%d.%d\n", (netif->ip_addr.addr >>  0) & 0xFF, 
                                             (netif->ip_addr.addr >>  8) & 0xFF,
                                             (netif->ip_addr.addr >> 16) & 0xFF,
                                             (netif->ip_addr.addr >> 24) & 0xFF);
#if LWIP_DNS
    if(netif->flags & NETIF_FLAG_DHCP)
    {
        struct ip_addr dns1 = dns_getserver(0);
        struct ip_addr dns2 = dns_getserver(1);
        
        lcd_printf("         dns1: %d.%d.%d.%d\n",(dns1.addr >>  0) & 0xFF, 
                                                  (dns1.addr >>  8) & 0xFF,
                                                  (dns1.addr >> 16) & 0xFF,
                                                  (dns1.addr >> 24) & 0xFF);
        
        lcd_printf("         dns2: %d.%d.%d.%d\n",(dns2.addr >>  0) & 0xFF, 
                                                  (dns2.addr >>  8) & 0xFF,
                                                  (dns2.addr >> 16) & 0xFF,
                                                  (dns2.addr >> 24) & 0xFF);
    }
#endif
#endif
}
HRESULT LWIP_SOCKETS_Driver::UpdateAdapterConfiguration( UINT32 interfaceIndex, UINT32 updateFlags, SOCK_NetworkConfiguration* config )
{
    NATIVE_PROFILE_PAL_NETWORK();
    if(interfaceIndex >= NETWORK_INTERFACE_COUNT) 
    {
        return CLR_E_INVALID_PARAMETER;
    }
    
    BOOL fEnableDhcp = (0 != (config->flags & SOCK_NETWORKCONFIGURATION_FLAGS_DHCP));
    BOOL fDynamicDns = (0 != (config->flags & SOCK_NETWORKCONFIGURATION_FLAGS_DYNAMIC_DNS));
    BOOL fDhcpStarted;

    struct netif *pNetIf = netif_find_interface(g_LWIP_SOCKETS_Driver.m_interfaces[interfaceIndex].m_interfaceNumber);
    if (NULL == pNetIf)
    {
        return CLR_E_FAIL;
    }

    fDhcpStarted = (0 != (pNetIf->flags & NETIF_FLAG_DHCP));

#if LWIP_DNS
    // when using DHCP do not use the static settings
    if(0 != (updateFlags & SOCK_NETWORKCONFIGURATION_UPDATE_DNS))
    {
        if(!fDynamicDns && (config->dnsServer1 != 0 || config->dnsServer2 != 0))
        {
            // user defined DNS addresses
            if(config->dnsServer1 != 0)
            {
                u8_t idx = 0;
                
                dns_setserver(idx, (struct ip_addr *)&config->dnsServer1);
            }
            if(config->dnsServer2 != 0)
            {
                u8_t idx = 1;

                dns_setserver(idx, (struct ip_addr *)&config->dnsServer2);
            }
        }
    }
#endif

#if LWIP_DHCP
    if(0 != (updateFlags & SOCK_NETWORKCONFIGURATION_UPDATE_DHCP))
    {
        if(fEnableDhcp)
        {   
            if(!fDhcpStarted)
            {
                if(ERR_OK != dhcp_start(pNetIf))
                {
                    return CLR_E_FAIL;
                }
            }
        }
        else
        {
            if(fDhcpStarted)
            {
                dhcp_stop(pNetIf);
            }

            netif_set_addr(pNetIf, (struct ip_addr *) &config->ipaddr, (struct ip_addr *)&config->subnetmask, (struct ip_addr *)&config->gateway);

            Network_PostEvent( NETWORK_EVENT_TYPE_ADDRESS_CHANGED, 0 );
        }
    }

    if(fEnableDhcp && fDhcpStarted)
    {
        // Try Renew before release since renewing after release will fail
        if(0 != (updateFlags & SOCK_NETWORKCONFIGURATION_UPDATE_DHCP_RENEW))
        {
            //netifapi_netif_common(pNetIf, NULL, dhcp_renew);
            dhcp_renew(pNetIf);
        }
        else if(0 != (updateFlags & SOCK_NETWORKCONFIGURATION_UPDATE_DHCP_RELEASE))
        {
            //netifapi_netif_common(pNetIf, NULL, dhcp_release);
            dhcp_release(pNetIf);
        }
    }
#endif

    if(0 != (updateFlags & SOCK_NETWORKCONFIGURATION_UPDATE_MAC))
    {
        int len = __min(config->macAddressLen, sizeof(pNetIf->hwaddr));
        
        memcpy(pNetIf->hwaddr, config->macAddressBuffer, len);
        pNetIf->hwaddr_len = len;

        // mac address requires stack re-init
        Network_Interface_Close(interfaceIndex);
        g_LWIP_SOCKETS_Driver.m_interfaces[interfaceIndex].m_interfaceNumber = Network_Interface_Open(interfaceIndex);
    }

    return S_OK;

}
void LWIP_SOCKETS_Driver::PostAvailabilityOff(void* arg)
{
	Network_PostEvent(NETWORK_EVENT_TYPE__AVAILABILITY_CHANGED, 0);
}
void LWIP_SOCKETS_Driver::PostAddressChanged(void* arg)
{
	Network_PostEvent(NETWORK_EVENT_TYPE_ADDRESS_CHANGED, 0);
}
void lwip_network_uptime_completion(void *arg)
{
    NATIVE_PROFILE_PAL_NETWORK();
    static BOOL isPhyPoweringUp = FALSE;
    static int nAttempts = 0;
    BOOL status;
    
    /* Power up PHY and wait while it starts */
    if (!LwipNetworkStatus && !isPhyPoweringUp)
    {
        eth_powerUpPhy(TRUE);
        isPhyPoweringUp = TRUE;
        LwipUpTimeCompletion.EnqueueDelta64( 2500000 );
        nAttempts++;
        return;
    }
    
    /* PHY should now have started, get the network status */
    isPhyPoweringUp = FALSE;
    status = eth_isPhyLinkValid(FALSE);

    /* Check whether network status has changed */
    if (status != LwipNetworkStatus)
    {
        struct netif* pNetIf = (struct netif*)arg;

        /* Check status */
        if (status)
        {   
            /* Network is up, open ethernet driver */
            SOCK_NetworkConfiguration *pNetCfg = &g_NetworkConfig.NetworkInterfaces[0];
            STM32F4_ETH_LWIP_open(pNetIf);
            netif_set_up( pNetIf );
            
            if(pNetCfg->flags & SOCK_NETWORKCONFIGURATION_FLAGS_DHCP)
            {
              dhcp_start( pNetIf );
            }
            
            nAttempts = 0;
            
            Network_PostEvent( NETWORK_EVENT_TYPE__AVAILABILITY_CHANGED, NETWORK_EVENT_FLAGS_IS_AVAILABLE );
        }
        else
        {
            /* Network is down, close ethernet driver */
            STM32F4_ETH_LWIP_close(FALSE);
            netif_set_down( (struct netif*)arg );
            Network_PostEvent( NETWORK_EVENT_TYPE__AVAILABILITY_CHANGED, 0);
        }
    
        /* Save new network status */
        LwipNetworkStatus = status;
    }

    /* Power down PHY if network is down */
    if (!LwipNetworkStatus)
    {
        eth_powerUpPhy(FALSE);
    }    
    
    /* Schedule the next network status check */
    if (LwipNetworkStatus || (nAttempts < 12) )
    {
        /* When network is up or has been up, check every 5 seconds */
        LwipUpTimeCompletion.EnqueueDelta64(  2500000 );
    }
    else
    {
        /* When network is down, check only once every minute */
        LwipUpTimeCompletion.EnqueueDelta64( 57500000 );
    }   
}