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