1 /*
2  * FreeRTOS V202212.00
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * https://www.FreeRTOS.org
23  * https://github.com/FreeRTOS
24  *
25  */
26 
27 /*
28  * A set of tasks are created that send UDP echo requests to the
29  * IP address set by the configECHO_SERVER_ADDR0 to
30  * configECHO_SERVER_ADDR_STRING constant, then wait for and verify the reply
31  *
32  * See the following web page for essential demo usage and configuration
33  * details:
34  * https://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/examples_FreeRTOS_simulator.html
35  */
36 
37 /* Standard includes. */
38 #include <stdint.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 
42 /* FreeRTOS includes. */
43 #include "FreeRTOS.h"
44 #include "task.h"
45 #include "queue.h"
46 
47 /* FreeRTOS+TCP includes. */
48 #include "FreeRTOS_IP.h"
49 #include "FreeRTOS_Sockets.h"
50 #include "FreeRTOS_IP_Private.h"
51 
52 
53 #define USE_ZERO_COPY           ( 1 )
54 
55 /* The echo tasks create a socket, send out a number of echo requests, listen
56  * for the echo reply, then close the socket again before starting over.  This
57  * delay is used between each iteration to ensure the network does not get too
58  * congested. */
59 #define echoLOOP_DELAY          pdMS_TO_TICKS( 2U )
60 
61 /* The number of instances of the echo client task to create. */
62 #define echoNUM_ECHO_CLIENTS    ( 1 )
63 
64 #define TX_RX_STR_SIZE          ( 25 )
65 
66 /* Rx and Tx time outs are used to ensure the sockets do not wait too long for
67  * missing data. */
68 static const TickType_t xReceiveTimeOut = pdMS_TO_TICKS( 4000 );
69 static const TickType_t xSendTimeOut = pdMS_TO_TICKS( 4000 );
70 static BaseType_t xHasStarted = pdFALSE;
71 
72 /*
73  * UDP echo client task
74  */
75 static void prvUDPEchoClientTask( void * pvParameters );
76 
vStartUDPEchoClientTasks_SingleTasks(uint16_t usTaskStackSize,UBaseType_t uxTaskPriority)77 void vStartUDPEchoClientTasks_SingleTasks( uint16_t usTaskStackSize,
78                                            UBaseType_t uxTaskPriority )
79 {
80     if( xHasStarted == pdFALSE )
81     {
82         BaseType_t xCount = 0;
83         BaseType_t x;
84 
85         xHasStarted = pdTRUE;
86 
87         /* Create the echo client tasks. */
88         for( x = 0; x < echoNUM_ECHO_CLIENTS; x++ )
89         {
90             char ucName[ 16 ];
91             snprintf( ucName, sizeof ucName, "echo_%02d", ( int ) x );
92             BaseType_t rc = xTaskCreate( prvUDPEchoClientTask, /* The function that implements the task. */
93                                          ucName,               /* Just a text name for the task to aid debugging. */
94                                          usTaskStackSize,      /* The stack size is defined in FreeRTOSIPConfig.h. */
95                                          ( void * ) x,         /* The task parameter, not used in this case. */
96                                          uxTaskPriority,       /* The priority assigned to the task is defined in FreeRTOSConfig.h. */
97                                          NULL );               /* The task handle is not used. */
98 
99             if( rc == pdPASS )
100             {
101                 xCount++;
102             }
103         }
104 
105         configPRINTF( ( "Started %d / %d tasks\n", ( int ) xCount, ( int ) echoNUM_ECHO_CLIENTS ) );
106     }
107     else
108     {
109         configPRINTF( ( "vStartUDPEchoClientTasks_SingleTasks: already started\n" ) );
110     }
111 }
112 /*-----------------------------------------------------------*/
113 
prvUDPEchoClientTask(void * pvParameters)114 static void prvUDPEchoClientTask( void * pvParameters )
115 {
116     Socket_t xSocket;
117     struct freertos_sockaddr xEchoServerAddress, xRxAddress;
118     int8_t cTxString[ TX_RX_STR_SIZE ], cRxString[ TX_RX_STR_SIZE ]; /* Make sure the stack is large enough to hold these.  Turn on stack overflow checking during debug to be sure. */
119     int32_t lLoopCount = 0UL;
120     int32_t lReturned;
121     const int32_t lMaxLoopCount = 50;
122     volatile uint32_t ulRxCount = 0UL, ulTxCount = 0UL;
123     uint32_t xAddressLength = sizeof( xEchoServerAddress );
124     BaseType_t xFamily = FREERTOS_AF_INET;
125     uint8_t ucIPType = ipTYPE_IPv4;
126 
127     /* Remove compiler warning about unused parameters. */
128     ( void ) pvParameters;
129 
130     memset( &xEchoServerAddress, 0, sizeof( xEchoServerAddress ) );
131     memset( &xRxAddress, 0, sizeof( xRxAddress ) );
132 
133     /* Echo requests are sent to the echo server.  The address of the echo
134      * server is configured by the constants configECHO_SERVER_ADDR0 to
135      * configECHO_SERVER_ADDR3 in FreeRTOSConfig.h. */
136 
137     #ifdef configECHO_SERVER_ADDR_STRING
138     {
139         BaseType_t rc = FreeRTOS_inet_pton( FREERTOS_AF_INET6, configECHO_SERVER_ADDR_STRING, ( void * ) xEchoServerAddress.sin_address.xIP_IPv6.ucBytes );
140 
141         if( rc == pdPASS )
142         {
143             xFamily = FREERTOS_AF_INET6;
144             ucIPType = ipTYPE_IPv6;
145         }
146         else
147         {
148             rc = FreeRTOS_inet_pton( FREERTOS_AF_INET4, configECHO_SERVER_ADDR_STRING, ( void * ) &( xEchoServerAddress.sin_address.ulIP_IPv4 ) );
149             configASSERT( rc == pdPASS );
150             xFamily = FREERTOS_AF_INET4;
151             ucIPType = ipTYPE_IPv4;
152         }
153     }
154     #else /* ifdef configECHO_SERVER_ADDR_STRING */
155     {
156         xEchoServerAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0, configECHO_SERVER_ADDR1, configECHO_SERVER_ADDR2, configECHO_SERVER_ADDR3 );
157     }
158     #endif /* ifdef configECHO_SERVER_ADDR_STRING */
159 
160     xEchoServerAddress.sin_len = sizeof( xEchoServerAddress );
161     xEchoServerAddress.sin_port = FreeRTOS_htons( configECHO_SERVER_PORT );
162     xEchoServerAddress.sin_family = xFamily;
163 
164     for( ; ; )
165     {
166         configPRINTF( ( "-------- Starting New Iteration --------\n" ) );
167         /* Create a socket. */
168         xSocket = FreeRTOS_socket( xFamily, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
169         configASSERT( xSocket != FREERTOS_INVALID_SOCKET );
170 
171         /* Set a time out so a missing reply does not cause the task to block
172          * indefinitely. */
173         FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );
174 
175         /* Send a number of echo requests. */
176         for( lLoopCount = 0; lLoopCount < lMaxLoopCount; lLoopCount++ )
177         {
178             /* Create the string that is sent to the echo server. */
179             sprintf( ( char * ) cTxString, "Message number %u\r\n", ulTxCount );
180 
181             #if USE_ZERO_COPY
182 
183                 /*
184                  * First obtain a buffer of adequate length from the TCP/IP stack into which
185                  * the string will be written. */
186                 uint8_t * pucBuffer = FreeRTOS_GetUDPPayloadBuffer_Multi( TX_RX_STR_SIZE, portMAX_DELAY, ucIPType );
187                 configASSERT( pucBuffer != NULL );
188                 memcpy( pucBuffer, &cTxString, strlen( ( const char * ) cTxString ) + 1 );
189 
190                 /* Send the string to the socket.  ulFlags is set to 0, so the zero
191                  * copy interface is not used.  That means the data from cTxString is
192                  * copied into a network buffer inside FreeRTOS_sendto(), and cTxString
193                  * can be reused as soon as FreeRTOS_sendto() has returned.  1 is added
194                  * to ensure the NULL string terminator is sent as part of the message. */
195                 lReturned = FreeRTOS_sendto( xSocket,                                  /* The socket being sent to. */
196                                              ( void * ) pucBuffer,                     /* The data being sent. */
197                                              strlen( ( const char * ) pucBuffer ) + 1, /* The length of the data being sent. */
198                                              FREERTOS_ZERO_COPY,                       /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */
199                                              &xEchoServerAddress,                      /* The destination address. */
200                                              sizeof( xEchoServerAddress ) );
201             #else /* if USE_ZERO_COPY */
202 
203                 /* Send the string to the socket.  ulFlags is set to 0, so the zero
204                  * copy interface is not used.  That means the data from cTxString is
205                  * copied into a network buffer inside FreeRTOS_sendto(), and cTxString
206                  * can be reused as soon as FreeRTOS_sendto() has returned.  1 is added
207                  * to ensure the NULL string terminator is sent as part of the message. */
208                 lReturned = FreeRTOS_sendto( xSocket,                                  /* The socket being sent to. */
209                                              ( void * ) cTxString,                     /* The data being sent. */
210                                              strlen( ( const char * ) cTxString ) + 1, /* The length of the data being sent. */
211                                              0,                                        /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */
212                                              &xEchoServerAddress,                      /* The destination address. */
213                                              sizeof( xEchoServerAddress ) );
214             #endif /* if USE_ZERO_COPY */
215 
216             if( lReturned == 0 )
217             {
218                 /* The send operation failed. */
219             }
220             else
221             {
222                 /* Keep a count of how many echo requests have been transmitted so
223                  * it can be compared to the number of echo replies received.  It would
224                  * be expected to loose at least one to an ARP message the first time
225                  * the	connection is created. */
226                 ulTxCount++;
227                 /* The send was successful. */
228                 FreeRTOS_debug_printf( ( "[Echo Client] Data sent...  \r\n" ) );
229             }
230 
231             /* Receive data echoed back to the socket.  ulFlags is zero, so the
232              * zero copy option is not being used and the received data will be
233              * copied into the buffer pointed to by cRxString.  xAddressLength is
234              * not actually used (at the time of writing this comment, anyway) by
235              * FreeRTOS_recvfrom(), but is set appropriately in case future
236              * versions do use it. */
237 
238             memset( ( void * ) cRxString, 0x00, sizeof( cRxString ) );
239 
240 
241             #if USE_ZERO_COPY
242                 uint8_t * pucReceivedUDPPayload = NULL;
243                 lReturned = FreeRTOS_recvfrom( xSocket,
244                                                &pucReceivedUDPPayload,
245                                                0,
246                                                FREERTOS_ZERO_COPY,
247                                                &xRxAddress,
248                                                &xAddressLength );
249 
250                 if( pucReceivedUDPPayload != NULL )
251                 {
252                     memcpy( ( void * ) ( cRxString ), pucReceivedUDPPayload, TX_RX_STR_SIZE );
253 
254                     FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucReceivedUDPPayload );
255                 }
256             #else /* if USE_ZERO_COPY */
257                 lReturned = FreeRTOS_recvfrom( xSocket,             /* The socket being received from. */
258                                                cRxString,           /* The buffer into which the received data will be written. */
259                                                sizeof( cRxString ), /* The size of the buffer provided to receive the data. */
260                                                0,                   /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */
261                                                &xRxAddress,         /* The address from where the data was sent (the source address). */
262                                                &xAddressLength );
263             #endif /* USE_ZERO_COPY */
264 
265             if( lReturned > 0 )
266             {
267                 /* Compare the transmitted string to the received string. */
268                 if( strcmp( ( char * ) cRxString, ( char * ) cTxString ) == 0 )
269                 {
270                     /* The echo reply was received without error. */
271                     ulRxCount++;
272 
273                     if( ( ulRxCount % 10 ) == 0 )
274                     {
275                         configPRINTF( ( "[Echo Client] Data was received correctly.\r\n" ) );
276                     }
277                 }
278                 else
279                 {
280                     FreeRTOS_debug_printf( ( "[Echo Client] Data received was erroneous.\r\n" ) );
281                 }
282             }
283             else
284             {
285                 FreeRTOS_debug_printf( ( "[Echo Client] Data was not received\r\n" ) );
286             }
287         }
288 
289         configPRINTF( ( "Exchange (Sent/Received) : %u/%u\n", ulTxCount, ulRxCount ) );
290         configPRINTF( ( "--------------------------------------\n\n" ) );
291 
292         /* Pause for a short while to ensure the network is not too
293          * congested. */
294         vTaskDelay( echoLOOP_DELAY );
295 
296         /* Close this socket before looping back to create another. */
297         FreeRTOS_closesocket( xSocket );
298     }
299 }
300