FreeRTOS-PLUS-TCP: Repeated eNetworkDownEvent events

Hi,

I noticed that once the network is down (for example after calling FreeRTOS_NetworkDown), my application gets eNetworkDownEvent events repeatedly every second. I would expect the network down event to behave in the same way as the network up event, which is sent only once after the network is up and running.

What is the goal of sending the network down event multiple times? Is it possible to disable it to just notify it once?

Thanks!

Hi TCP-newbie, thank you for your interest in FreeRTOS+TCP.

Here is a summary about the network-down events:

The function vCheckNetworkTimers() will be called in every IP-task loop. As long as one or more interfaces are not up, it will initiate a network down event.

  1. It will call FreeRTOS_NetworkDown() for a every interface which is down
  2. This function will send a eNetworkDownEvent to the IP-task
  3. It will check all endpoint of mentioned interface
  4. For each endpoint, bEndPointUp will be cleared
  5. For each endpoint vApplicationIPNetworkEventHook_Multi() will be called with the parameter eNetworkDown
  6. And then, most importantly, pxInterface->pfInitialise() is called to check the interface.

So it is normal that the down-event is repeated as long as pxInterface->pfInitialise() fails.
The interval at which the event is repeated can be configured with:

/** @brief Time delay between repeated attempts to
  * initialise the network hardware. */
#ifndef ipINITIALISATION_RETRY_DELAY
    #define ipINITIALISATION_RETRY_DELAY    ( pdMS_TO_TICKS( 3000U ) )
#endif

PS. there are two minor issues:

  1. The macro ipINITIALISATION_RETRY_DELAY is defined locally in FreeRTOS_IP_Utils.c, but it is never used.
  2. In FreeRTOS_IP.c, the macro definition uses pdMS_TO_TICKS(), and when applying, again pdMS_TO_TICKS()

In all newer code, timing macro’s have an extension _TICKS or _MS, which makes clear if pdMS_TO_TICKS() has to be called.

About the network interface. Many developers think that pfInitialise() will be called only once, right after start-up. That is not true.

The function xxx_NetworkInterfaceInitialise() will be called as long as it returns pdFAIL.

Here is an example, borrowe dfrom the STM32Fx driver:

static BaseType_t xSTM32F_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface )
{
    if( xMacInitStatus == eMACInit )
    {
        /* Initialisation is only done once. */
        if( ethernet_init() == pdPASS )
        {
            xMacInitStatus = eMACPass;
        }
        else
        {
            xMacInitStatus = eMACFailed;
        }
    } /* if( xMacInitStatus == eMACInit ) */

    if( xMacInitStatus != eMACPass )
    {
        /* EMAC initialisation failed, return pdFAIL.
         * This interface will not get into a working state anymore. */
        xResult = pdFAIL;
    }
    else
    {
        if( xPhyObject.ulLinkStatusMask != 0U )
        {
            xResult = pdPASS;
        }
        else
        {
            /* For now pdFAIL will be returned. But prvEMACHandlerTask() is running
             * and it will keep on checking the PHY and set 'ulLinkStatusMask' when necessary. */
            xResult = pdFAIL;
        }
    }

    /* When returning pdPASS, the stack will become active and
     * start DHCP (in configured).
     * Otherwise, this function will be called again. */
    return xResult;
}

If you want, you can ignore the repeated events like eg. here:

static volatile BaseType_t xServerCanStart;  /* Becomes true when the internet service can start-up. */

void vApplicationIPNetworkEventHook_Multi( eIPCallbackEvent_t eNetworkEvent,
                                           struct xNetworkEndPoint * pxEndPoint )
{
    static BaseType_t xLastEvent = -1;
    if( xLastEvent != ( BaseType_t ) eNetworkEvent )
    {
        xLastEvent = ( BaseType_t ) eNetworkEvent;
        if( eNetworkEvent == eNetworkUp )
        {
            xServerCanStart = pdTRUE;
        }
    }
    else
    {
        /* Ignore this repeated event. */
    }
}
/*-----------------------------------------------------------*/
1 Like

Thanks @htibosch for the quick and detailed answer.

I understand I can ignore the event in vApplicationIPNetworkEventHook_Multi but I was curious if there was a way to just get a single eNetworkDown event instead of multiple events periodically (to be consistent with eNetworkUp).

My software will turn on and off the network interface on demand (and keep the IP task running) so the stream of network down events is a bit annoying but I don’t want to ignore it all the way since I want to capture the first one at least. I know I can implement some logic to achieve that in the vApplicationIPNetworkEventHook_Multi but maybe there was already a way to configure +TCP to do that but couldn’t find it.

Thanks!

I was curious if there was a way to just get a single eNetworkDown event instead of multiple events periodically (to be consistent with eNetworkUp).

I totally agree with you. The polling of the device , by calling:

    if( pxInterface->pfInitialise( pxInterface ) == pdPASS )
    {
        pxInterface->bits.bInterfaceUp = pdTRUE_UNSIGNED;
    }

is essential. It wants to know when the Link Status becomes high again.

But other actions, like:

    vManageSolicitedNodeAddress( pxEndPoint, pdFALSE );
    vApplicationIPNetworkEventHook_Multi( eNetworkDown, pxEndPoint );
    FreeRTOS_ClearARP( pxEndPoint );
    vDHCPStop( pxEndPoint );
    vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE );

don’t have any reason. Only when bEndPointUp goes down, the actions must be taken.

1 Like