ipconfigSOCKET_HAS_USER_WAKE_CALLBACK only works on TCP sockets?

Hi,

Does the ipconfigSOCKET_HAS_USER_WAKE_CALLBACK functionality only work for TCP sockets?

I configured a UDP socket with FreeRTOS_setsockopt(..., FREERTOS_SO_WAKEUP_CALLBACK, mycallback, 0) but never get any call to mycallback on recv/send/etc.

I know I could use FREERTOS_SO_UDP_RECV_HANDLER but I am trying to use FREERTOS_SO_WAKEUP_CALLBACK since I have a TCP socket already using it.

BTW, FREERTOS_SO_TCP_RECV_HANDLER seems to always consume the data (i.e the IP task doesn’t put the TCP received data in the socket stream when using that callback). That is why I need to use FREERTOS_SO_WAKEUP_CALLBACK on my TCP socket.

I am trying to use the same strategy for both and reduce code size by disabling ipconfigUSE_CALLBACKS.

Thanks!

@rtos

Does the ipconfigSOCKET_HAS_USER_WAKE_CALLBACK functionality only work for TCP sockets?

Yes, the socket option FREERTOS_SO_WAKEUP_CALLBACK currently is used to notify about
TCP socket events/state changes.

For UDP sockets, you can handle the data in the network buffer directly with the FREERTOS_SO_UDP_RECV_HANDLER and the packet will not be added to the sockets xWaitingPacketsList, ie packet wont be available to the FreeRTOS_recvfrom. Note that the handler is called from the context of IP task and not the application task.

FREERTOS_SO_TCP_RECV_HANDLER seems to always consume the data (i.e the IP task doesn’t put the TCP received data in the socket stream when using that callback).

If receive handler is installed with the FREERTOS_SO_TCP_RECV_HANDLER socket option, the received TCP data should be handled by the handler directly once the data to a stream buffer is added. The pointer to the RX-stream of the socket is passed to the handler. If the handler is present, the receive event is not set for that socket when there is data; hence, the IP task won’t wake up the socket.

That is why I need to use FREERTOS_SO_WAKEUP_CALLBACK on my TCP socket.

Can you elaborate the problem that you are trying to solve?

the socket option FREERTOS_SO_WAKEUP_CALLBACK currently is used to notify about TCP socket events/state changes.

That is right. It would have been simple to extend it to UDP as well, but we didn’t.

Using FREERTOS_SO_UDP_RECV_HANDLER and FREERTOS_SO_UDP_SENT_HANDLER is indeed a good alternative.

Personally I like the use of a semaphore:

SemaphoreHandle_t xSemaphore = xSemaphoreCreateBinary();

#if ( ipconfigSOCKET_HAS_USER_SEMAPHORE == 1 )
    FreeRTOS_setsockopt( xSocket,
                         0,
                         FREERTOS_SO_SET_SEMAPHORE,
                         ( void * ) &xSemaphore,
                         sizeof( xSemaphore ) );
#endif
    void mainLoop()
    {
        TickType_t xTimeOut = pdMS_TO_TICKS( 200 );
        for( ;; )
        {
            if( xSemaphoreTake( xSemaphore, xTimeOut ) == pdPASS )
            {
                /* Check the active sockets. */
            }
        }
    }

Thanks @tony-josi-aws @htibosch for the answers.

For UDP sockets, you can handle the data in the network buffer directly with the FREERTOS_SO_UDP_RECV_HANDLER and the packet will not be added to the sockets xWaitingPacketsList ,

Thanks, I’ll try to do some more research on FREERTOS_SO_UDP_RECV_HANDLER. My plan was not to overload the IP task and handle all input data in another lower-priority task. Also, I was concerned about a flood of UDP packets being processed directly in the handler and stressing the IP task. If I handle the data in another task (via recvfrom) at least the number of UDP packets is capped to ipconfigUDP_MAX_RX_PACKETS and that hopefully releases the IP task faster under that scenario.

Personally I like the use of a semaphore:

Was trying to avoid that for two reasons:

  1. The task I use to handle the incoming data also needs to block in a task notification index
  2. Semaphores are not as light weight as notifications. In my TCP handler I just wake up my task via xTaskNotifyGiveIndexed and only on ‘receive’ events, semaphore seems to get updated on every socket event.

Any change we can setsockopt with a notification index in a future release :slight_smile: ?

Thanks!

When UDP packets are received, all the IP task does is do some basic processing (ARP resolution/refresh, CRC, etc.), add the packet to the socket’s waiting packet list (no copy involved), & notify the application task (low priority tasks wating on FreeRTOS_recvfrom) through the UDP socket’s eSOCKET_RECEIVE event bit. There is no packet copy (memcpy) happening in the IP task; the copying happens in the user application task once the eSOCKET_RECEIVE event is received by the user application via FreeRTOS_recvfrom call. Hence almost everything thats time expensive is deferred to the application task already.

You can further reduce the number of copies by using the zero copy mechanism of the socket API - Receiving UDP Data (zero copy interface) by directly receiving the arrived network buffer (and hence the ownership) from the IP task.

1 Like

My plan was not to overload the IP task and handle all input data in another lower-priority task

Good idea. Also it should be noted that within an application hook, you can not use the IP-task because you are called from the IP-task. So if you block, the task might block for ever.

The only thing you can do is to notify a task and set a LED :slight_smile:

Any change we can setsockopt with a notification index in a future release

We already have, kind of :

Remember that you can assign an ID to each socket. This ID is stored as a void pointer, but you can interpret it as an object, or a task handle or whatever.

    BaseType_t xSocketSetSocketID( const Socket_t xSocket,
                                   void * pvSocketID );
    void * pvSocketGetSocketID( const ConstSocket_t xSocket );

Your handler sees which socket has received a packet, and it can lookup which task or which notify index must be woken up.

struct {
    TaskHandle_t pxHandle;
    BaseType xNotifyIndex;
} Socket_ID_t;

BaseType_t onUDPReceive( Socket_t xSocket,
                         void * pData,
                         size_t xLength,
                         const struct freertos_sockaddr * pxFrom,
                         const struct freertos_sockaddr * pxDest )
{
    void * ptr = pvSocketGetSocketID( xSocket );
    if( ptr != NULL )
    {
        Socket_ID_t * id = ( Socket_ID_t * ) ptr;
        xTaskNotifyIndexed( id->pxHandle, 0, 1U << id->xNotifyIndex, eSetBits );
    }
    /* IMPORTANT */  
    /* Return false to indicate that the incoming packet has not been consumed yet. */
    return pdFALSE;
}

Do you understand what I mean?

1 Like