/* * FreeRTOS V202212.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * 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. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /****************************************************************************** * * See the following web page for essential TwoEchoClient.c usage and * configuration details: * https://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_UDP/Embedded_Ethernet_Examples/Common_Echo_Clients.shtml * ******************************************************************************/ /* Standard includes. */ #include #include #include /* FreeRTOS includes. */ #include "FreeRTOS.h" #include "task.h" /* FreeRTOS+UDP includes. */ #include "FreeRTOS_UDP_IP.h" #include "FreeRTOS_Sockets.h" /* Small delay used between attempts to obtain a zero copy buffer. */ #define echoTINY_DELAY ( ( TickType_t ) 2 ) /* The echo tasks create a socket, send out a number of echo requests * (listening for each echo reply), then close the socket again before * starting over. This delay is used between each iteration to ensure the * network does not get too congested. */ #define echoLOOP_DELAY ( ( TickType_t ) 250 / portTICK_RATE_MS ) #if ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 /* When the trace recorder code is included user events are generated to * mark the sending and receiving of the echoed data (only in the zero copy * task. */ #define echoMARK_SEND_IN_TRACE_BUFFER( x ) vTraceUserEvent( x ) traceLabel xZeroCopySendEvent, xZeroCopyReceiveEvent; #else /* When the trace recorder code is not included just #define away the call * to post the user event. */ #define echoMARK_SEND_IN_TRACE_BUFFER( x ) #define xZeroCopySendEvent 0 #define xZeroCopyReceiveEvent 0 #endif /* The echo server is assumed to be on port 7, which is the standard echo * protocol port. */ #define echoECHO_PORT ( 7 ) /* * Uses a socket to send data to, then receive data from, the standard echo * port number 7. prvEchoClientTask() uses the standard interface. * prvZeroCopyEchoClientTask() uses the zero copy interface. */ static void prvEchoClientTask( void * pvParameters ); static void prvZeroCopyEchoClientTask( void * pvParameters ); /* The receive timeout is set shorter when the windows simulator is used * because simulated time is slower than real time. */ #ifdef _WINDOWS_ const TickType_t xReceiveTimeOut = 50 / portTICK_RATE_MS; #else const TickType_t xReceiveTimeOut = 500 / portTICK_RATE_MS; #endif /*-----------------------------------------------------------*/ void vStartEchoClientTasks( uint16_t usTaskStackSize, UBaseType_t uxTaskPriority ) { /* Create the echo client task that does not use the zero copy interface. */ xTaskCreate( prvEchoClientTask, /* The function that implements the task. */ "Echo0", /* Just a text name for the task to aid debugging. */ usTaskStackSize, /* The stack size is defined in FreeRTOSIPConfig.h. */ NULL, /* The task parameter, not used in this case. */ uxTaskPriority, /* The priority assigned to the task is defined in FreeRTOSConfig.h. */ NULL ); /* The task handle is not used. */ /* Create the echo client task that does use the zero copy interface. */ xTaskCreate( prvZeroCopyEchoClientTask, /* The function that implements the task. */ "Echo1", /* Just a text name for the task to aid debugging. */ usTaskStackSize, /* The stack size is defined in FreeRTOSIPConfig.h. */ NULL, /* The task parameter, not used in this case. */ uxTaskPriority, /* The priority assigned to the task is defined in FreeRTOSConfig.h. */ NULL ); /* The task handle is not used. */ } /*-----------------------------------------------------------*/ static void prvEchoClientTask( void * pvParameters ) { xSocket_t xSocket; struct freertos_sockaddr xEchoServerAddress; char cTxString[ 25 ], cRxString[ 25 ]; /* Make sure the stack is large enough to hold these. Turn on stack overflow checking during debug to be sure. */ int32_t lLoopCount = 0UL; const int32_t lMaxLoopCount = 50; volatile uint32_t ulRxCount = 0UL, ulTxCount = 0UL; uint32_t xAddressLength = sizeof( xEchoServerAddress ); /* Remove compiler warning about unused parameters. */ ( void ) pvParameters; /* Echo requests are sent to the echo server. The address of the echo * server is configured by the constants configECHO_SERVER_ADDR0 to * configECHO_SERVER_ADDR3 in FreeRTOSConfig.h. */ xEchoServerAddress.sin_port = FreeRTOS_htons( echoECHO_PORT ); #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 ) { xEchoServerAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0, configECHO_SERVER_ADDR1, configECHO_SERVER_ADDR2, configECHO_SERVER_ADDR3 ); } #else { xEchoServerAddress.sin_addr = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0, configECHO_SERVER_ADDR1, configECHO_SERVER_ADDR2, configECHO_SERVER_ADDR3 ); } #endif /* defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 ) */ xEchoServerAddress.sin_family = FREERTOS_AF_INET; for( ; ; ) { /* Create a socket. */ xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); configASSERT( xSocket != FREERTOS_INVALID_SOCKET ); /* Set a time out so a missing reply does not cause the task to block * indefinitely. */ FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) ); /* Send a number of echo requests. */ for( lLoopCount = 0; lLoopCount < lMaxLoopCount; lLoopCount++ ) { /* Create the string that is sent to the echo server. */ sprintf( cTxString, "Message number %u\r\n", ( unsigned int ) ulTxCount ); /* Send the string to the socket. ulFlags is set to 0, so the zero * copy interface is not used. That means the data from cTxString is * copied into a network buffer inside FreeRTOS_sendto(), and cTxString * can be reused as soon as FreeRTOS_sendto() has returned. 1 is added * to ensure the NULL string terminator is sent as part of the message. */ FreeRTOS_sendto( xSocket, /* The socket being sent to. */ ( void * ) cTxString, /* The data being sent. */ strlen( cTxString ) + 1, /* The length of the data being sent. */ 0, /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */ &xEchoServerAddress, /* The destination address. */ sizeof( xEchoServerAddress ) ); /* Keep a count of how many echo requests have been transmitted so * it can be compared to the number of echo replies received. It would * be expected to loose at least one to an ARP message the first time * the connection is created. */ ulTxCount++; /* Receive data echoed back to the socket. ulFlags is zero, so the * zero copy option is not being used and the received data will be * copied into the buffer pointed to by cRxString. xAddressLength is * not actually used (at the time of writing this comment, anyway) by * FreeRTOS_recvfrom(), but is set appropriately in case future * versions do use it. */ memset( ( void * ) cRxString, 0x00, sizeof( cRxString ) ); FreeRTOS_recvfrom( xSocket, /* The socket being received from. */ cRxString, /* The buffer into which the received data will be written. */ sizeof( cRxString ), /* The size of the buffer provided to receive the data. */ 0, /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */ &xEchoServerAddress, /* The address from where the data was sent (the source address). */ &xAddressLength ); /* Compare the transmitted string to the received string. */ if( strcmp( cRxString, cTxString ) == 0 ) { /* The echo reply was received without error. */ ulRxCount++; } } /* Pause for a short while to ensure the network is not too * congested. */ vTaskDelay( echoLOOP_DELAY ); /* Close this socket before looping back to create another. */ FreeRTOS_closesocket( xSocket ); } } /*-----------------------------------------------------------*/ static void prvZeroCopyEchoClientTask( void * pvParameters ) { xSocket_t xSocket; struct freertos_sockaddr xEchoServerAddress; static char cTxString[ 40 ]; int32_t lLoopCount = 0UL; volatile uint32_t ulRxCount = 0UL, ulTxCount = 0UL; uint32_t xAddressLength = sizeof( xEchoServerAddress ); int32_t lReturned; uint8_t * pucUDPPayloadBuffer; const int32_t lMaxLoopCount = 50; const char * const pcStringToSend = "Zero copy message number"; /* The buffer is large enough to hold the string, a number, and the string terminator. */ const size_t xBufferLength = strlen( pcStringToSend ) + 15; #if ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 { /* When the trace recorder code is included user events are generated to * mark the sending and receiving of the echoed data (only in the zero copy * task). */ xZeroCopySendEvent = xTraceOpenLabel( "ZeroCopyTx" ); xZeroCopyReceiveEvent = xTraceOpenLabel( "ZeroCopyRx" ); } #endif /* ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS */ /* Remove compiler warning about unused parameters. */ ( void ) pvParameters; /* Delay for a little while to ensure the task is out of synch with the * other echo task implemented above. */ vTaskDelay( echoLOOP_DELAY >> 1 ); /* Echo requests are sent to the echo server. The address of the echo * server is configured by the constants configECHO_SERVER_ADDR0 to * configECHO_SERVER_ADDR3 in FreeRTOSConfig.h. */ xEchoServerAddress.sin_port = FreeRTOS_htons( echoECHO_PORT ); #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 ) { xEchoServerAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0, configECHO_SERVER_ADDR1, configECHO_SERVER_ADDR2, configECHO_SERVER_ADDR3 ); } #else { xEchoServerAddress.sin_addr = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0, configECHO_SERVER_ADDR1, configECHO_SERVER_ADDR2, configECHO_SERVER_ADDR3 ); } #endif /* defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 ) */ xEchoServerAddress.sin_family = FREERTOS_AF_INET; for( ; ; ) { /* Create a socket. */ xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); configASSERT( xSocket != FREERTOS_INVALID_SOCKET ); /* Set a time out so a missing reply does not cause the task to block * indefinitely. */ FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) ); /* Send a number of echo requests. */ for( lLoopCount = 0; lLoopCount < lMaxLoopCount; lLoopCount++ ) { /* This task is going to send using the zero copy interface. The * data being sent is therefore written directly into a buffer that is * passed by reference into the FreeRTOS_sendto() function. First * obtain a buffer of adequate size from the IP stack. Although a max * delay is used, the actual delay will be capped to * ipconfigMAX_SEND_BLOCK_TIME_TICKS, hence the test to ensure a buffer * was actually obtained. */ pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xBufferLength, portMAX_DELAY ); if( pucUDPPayloadBuffer != NULL ) { /* A buffer was successfully obtained. Create the string that is * sent to the echo server. Note the string is written directly * into the buffer obtained from the IP stack. */ sprintf( ( char * ) pucUDPPayloadBuffer, "%s %u\r\n", "Zero copy message number", ( unsigned int ) ulTxCount ); /* Also copy the string into a local buffer so it can be compared * with the string that is later received back from the echo server. */ strcpy( cTxString, ( char * ) pucUDPPayloadBuffer ); /* Pass the buffer into the send function. ulFlags has the * FREERTOS_ZERO_COPY bit set so the IP stack will take control of * the buffer, rather than copy data out of the buffer. */ echoMARK_SEND_IN_TRACE_BUFFER( xZeroCopySendEvent ); lReturned = FreeRTOS_sendto( xSocket, /* The socket being sent to. */ ( void * ) pucUDPPayloadBuffer, /* The buffer being passed into the IP stack. */ strlen( cTxString ) + 1, /* The length of the data being sent. Plus 1 to ensure the null terminator is part of the data. */ FREERTOS_ZERO_COPY, /* ulFlags with the zero copy bit is set. */ &xEchoServerAddress, /* Where the data is being sent. */ sizeof( xEchoServerAddress ) ); if( lReturned == 0 ) { /* The send operation failed, so this task is still * responsible for the buffer obtained from the IP stack. To * ensure the buffer is not lost it must either be used again, * or, as in this case, returned to the IP stack using * FreeRTOS_ReleaseUDPPayloadBuffer(). pucUDPPayloadBuffer can * be safely re-used to receive from the socket below once the * buffer has been returned to the stack. */ FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer ); } else { /* The send was successful so the IP stack is now managing * the buffer pointed to by pucUDPPayloadBuffer, and the IP * stack will return the buffer once it has been sent. * pucUDPPayloadBuffer can be safely re-used to receive from * the socket below. */ } /* Keep a count of how many echo requests have been transmitted * so it can be compared to the number of echo replies received. * It would be expected to loose at least one to an ARP message the * first time the connection is created. */ ulTxCount++; /* Receive data on the socket. ulFlags has the zero copy bit set * (FREERTOS_ZERO_COPY) indicating to the stack that a reference to * the received data should be passed out to this task using the * second parameter to the FreeRTOS_recvfrom() call. When this is * done the IP stack is no longer responsible for releasing the * buffer, and the task *must* return the buffer to the stack when * it is no longer needed. By default the receive block time is * portMAX_DELAY. */ echoMARK_SEND_IN_TRACE_BUFFER( xZeroCopyReceiveEvent ); lReturned = FreeRTOS_recvfrom( xSocket, /* The socket to receive from. */ ( void * ) &pucUDPPayloadBuffer, /* pucUDPPayloadBuffer will be set to point to the buffer that already contains the received data. */ 0, /* Ignored because the zero copy interface is being used. */ FREERTOS_ZERO_COPY, /* ulFlags with the FREERTOS_ZERO_COPY bit set. */ &xEchoServerAddress, /* The address from which the data was sent. */ &xAddressLength ); if( lReturned > 0 ) { /* Compare the string sent to the echo server with the string * received back from the echo server. */ if( strcmp( ( char * ) pucUDPPayloadBuffer, cTxString ) == 0 ) { /* The strings matched. */ ulRxCount++; } /* The buffer that contains the data passed out of the stack * must* be returned to the stack. */ FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); } } } /* Pause for a short while to ensure the network is not too * congested. */ vTaskDelay( echoLOOP_DELAY ); /* Close this socket before looping back to create another. */ FreeRTOS_closesocket( xSocket ); } } /*-----------------------------------------------------------*/