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 /******************************************************************************
29 *
30 * See the following web page for essential TwoEchoClient.c usage and
31 * configuration details:
32 * https://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_UDP/Embedded_Ethernet_Examples/Common_Echo_Clients.shtml
33 *
34 ******************************************************************************/
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
46 /* FreeRTOS+UDP includes. */
47 #include "FreeRTOS_UDP_IP.h"
48 #include "FreeRTOS_Sockets.h"
49
50 /* Small delay used between attempts to obtain a zero copy buffer. */
51 #define echoTINY_DELAY ( ( TickType_t ) 2 )
52
53 /* The echo tasks create a socket, send out a number of echo requests
54 * (listening for each echo reply), then close the socket again before
55 * starting over. This delay is used between each iteration to ensure the
56 * network does not get too congested. */
57 #define echoLOOP_DELAY ( ( TickType_t ) 250 / portTICK_RATE_MS )
58
59 #if ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1
60
61 /* When the trace recorder code is included user events are generated to
62 * mark the sending and receiving of the echoed data (only in the zero copy
63 * task. */
64 #define echoMARK_SEND_IN_TRACE_BUFFER( x ) vTraceUserEvent( x )
65 traceLabel xZeroCopySendEvent, xZeroCopyReceiveEvent;
66
67 #else
68
69 /* When the trace recorder code is not included just #define away the call
70 * to post the user event. */
71 #define echoMARK_SEND_IN_TRACE_BUFFER( x )
72 #define xZeroCopySendEvent 0
73 #define xZeroCopyReceiveEvent 0
74 #endif
75
76 /* The echo server is assumed to be on port 7, which is the standard echo
77 * protocol port. */
78 #define echoECHO_PORT ( 7 )
79
80 /*
81 * Uses a socket to send data to, then receive data from, the standard echo
82 * port number 7. prvEchoClientTask() uses the standard interface.
83 * prvZeroCopyEchoClientTask() uses the zero copy interface.
84 */
85 static void prvEchoClientTask( void * pvParameters );
86 static void prvZeroCopyEchoClientTask( void * pvParameters );
87
88 /* The receive timeout is set shorter when the windows simulator is used
89 * because simulated time is slower than real time. */
90 #ifdef _WINDOWS_
91 const TickType_t xReceiveTimeOut = 50 / portTICK_RATE_MS;
92 #else
93 const TickType_t xReceiveTimeOut = 500 / portTICK_RATE_MS;
94 #endif
95
96 /*-----------------------------------------------------------*/
97
vStartEchoClientTasks(uint16_t usTaskStackSize,UBaseType_t uxTaskPriority)98 void vStartEchoClientTasks( uint16_t usTaskStackSize,
99 UBaseType_t uxTaskPriority )
100 {
101 /* Create the echo client task that does not use the zero copy interface. */
102 xTaskCreate( prvEchoClientTask, /* The function that implements the task. */
103 "Echo0", /* Just a text name for the task to aid debugging. */
104 usTaskStackSize, /* The stack size is defined in FreeRTOSIPConfig.h. */
105 NULL, /* The task parameter, not used in this case. */
106 uxTaskPriority, /* The priority assigned to the task is defined in FreeRTOSConfig.h. */
107 NULL ); /* The task handle is not used. */
108
109 /* Create the echo client task that does use the zero copy interface. */
110 xTaskCreate( prvZeroCopyEchoClientTask, /* The function that implements the task. */
111 "Echo1", /* Just a text name for the task to aid debugging. */
112 usTaskStackSize, /* The stack size is defined in FreeRTOSIPConfig.h. */
113 NULL, /* The task parameter, not used in this case. */
114 uxTaskPriority, /* The priority assigned to the task is defined in FreeRTOSConfig.h. */
115 NULL ); /* The task handle is not used. */
116 }
117 /*-----------------------------------------------------------*/
118
prvEchoClientTask(void * pvParameters)119 static void prvEchoClientTask( void * pvParameters )
120 {
121 xSocket_t xSocket;
122 struct freertos_sockaddr xEchoServerAddress;
123 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. */
124 int32_t lLoopCount = 0UL;
125 const int32_t lMaxLoopCount = 50;
126 volatile uint32_t ulRxCount = 0UL, ulTxCount = 0UL;
127 uint32_t xAddressLength = sizeof( xEchoServerAddress );
128
129 /* Remove compiler warning about unused parameters. */
130 ( void ) pvParameters;
131
132 /* Echo requests are sent to the echo server. The address of the echo
133 * server is configured by the constants configECHO_SERVER_ADDR0 to
134 * configECHO_SERVER_ADDR3 in FreeRTOSConfig.h. */
135 xEchoServerAddress.sin_port = FreeRTOS_htons( echoECHO_PORT );
136
137 #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 )
138 {
139 xEchoServerAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0,
140 configECHO_SERVER_ADDR1,
141 configECHO_SERVER_ADDR2,
142 configECHO_SERVER_ADDR3 );
143 }
144 #else
145 {
146 xEchoServerAddress.sin_addr = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0,
147 configECHO_SERVER_ADDR1,
148 configECHO_SERVER_ADDR2,
149 configECHO_SERVER_ADDR3 );
150 }
151 #endif /* defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 ) */
152
153 xEchoServerAddress.sin_family = FREERTOS_AF_INET;
154
155 for( ; ; )
156 {
157 /* Create a socket. */
158 xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
159 configASSERT( xSocket != FREERTOS_INVALID_SOCKET );
160
161 /* Set a time out so a missing reply does not cause the task to block
162 * indefinitely. */
163 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );
164
165 /* Send a number of echo requests. */
166 for( lLoopCount = 0; lLoopCount < lMaxLoopCount; lLoopCount++ )
167 {
168 /* Create the string that is sent to the echo server. */
169 sprintf( cTxString, "Message number %u\r\n", ( unsigned int ) ulTxCount );
170
171 /* Send the string to the socket. ulFlags is set to 0, so the zero
172 * copy interface is not used. That means the data from cTxString is
173 * copied into a network buffer inside FreeRTOS_sendto(), and cTxString
174 * can be reused as soon as FreeRTOS_sendto() has returned. 1 is added
175 * to ensure the NULL string terminator is sent as part of the message. */
176 FreeRTOS_sendto( xSocket, /* The socket being sent to. */
177 ( void * ) cTxString, /* The data being sent. */
178 strlen( cTxString ) + 1, /* The length of the data being sent. */
179 0, /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */
180 &xEchoServerAddress, /* The destination address. */
181 sizeof( xEchoServerAddress ) );
182
183 /* Keep a count of how many echo requests have been transmitted so
184 * it can be compared to the number of echo replies received. It would
185 * be expected to loose at least one to an ARP message the first time
186 * the connection is created. */
187 ulTxCount++;
188
189 /* Receive data echoed back to the socket. ulFlags is zero, so the
190 * zero copy option is not being used and the received data will be
191 * copied into the buffer pointed to by cRxString. xAddressLength is
192 * not actually used (at the time of writing this comment, anyway) by
193 * FreeRTOS_recvfrom(), but is set appropriately in case future
194 * versions do use it. */
195 memset( ( void * ) cRxString, 0x00, sizeof( cRxString ) );
196 FreeRTOS_recvfrom( xSocket, /* The socket being received from. */
197 cRxString, /* The buffer into which the received data will be written. */
198 sizeof( cRxString ), /* The size of the buffer provided to receive the data. */
199 0, /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */
200 &xEchoServerAddress, /* The address from where the data was sent (the source address). */
201 &xAddressLength );
202
203 /* Compare the transmitted string to the received string. */
204 if( strcmp( cRxString, cTxString ) == 0 )
205 {
206 /* The echo reply was received without error. */
207 ulRxCount++;
208 }
209 }
210
211 /* Pause for a short while to ensure the network is not too
212 * congested. */
213 vTaskDelay( echoLOOP_DELAY );
214
215 /* Close this socket before looping back to create another. */
216 FreeRTOS_closesocket( xSocket );
217 }
218 }
219 /*-----------------------------------------------------------*/
220
prvZeroCopyEchoClientTask(void * pvParameters)221 static void prvZeroCopyEchoClientTask( void * pvParameters )
222 {
223 xSocket_t xSocket;
224 struct freertos_sockaddr xEchoServerAddress;
225 static char cTxString[ 40 ];
226 int32_t lLoopCount = 0UL;
227 volatile uint32_t ulRxCount = 0UL, ulTxCount = 0UL;
228 uint32_t xAddressLength = sizeof( xEchoServerAddress );
229 int32_t lReturned;
230 uint8_t * pucUDPPayloadBuffer;
231
232 const int32_t lMaxLoopCount = 50;
233 const char * const pcStringToSend = "Zero copy message number";
234 /* The buffer is large enough to hold the string, a number, and the string terminator. */
235 const size_t xBufferLength = strlen( pcStringToSend ) + 15;
236
237 #if ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1
238 {
239 /* When the trace recorder code is included user events are generated to
240 * mark the sending and receiving of the echoed data (only in the zero copy
241 * task). */
242 xZeroCopySendEvent = xTraceOpenLabel( "ZeroCopyTx" );
243 xZeroCopyReceiveEvent = xTraceOpenLabel( "ZeroCopyRx" );
244 }
245 #endif /* ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS */
246
247 /* Remove compiler warning about unused parameters. */
248 ( void ) pvParameters;
249
250 /* Delay for a little while to ensure the task is out of synch with the
251 * other echo task implemented above. */
252 vTaskDelay( echoLOOP_DELAY >> 1 );
253
254 /* Echo requests are sent to the echo server. The address of the echo
255 * server is configured by the constants configECHO_SERVER_ADDR0 to
256 * configECHO_SERVER_ADDR3 in FreeRTOSConfig.h. */
257 xEchoServerAddress.sin_port = FreeRTOS_htons( echoECHO_PORT );
258
259 #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 )
260 {
261 xEchoServerAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0,
262 configECHO_SERVER_ADDR1,
263 configECHO_SERVER_ADDR2,
264 configECHO_SERVER_ADDR3 );
265 }
266 #else
267 {
268 xEchoServerAddress.sin_addr = FreeRTOS_inet_addr_quick( configECHO_SERVER_ADDR0,
269 configECHO_SERVER_ADDR1,
270 configECHO_SERVER_ADDR2,
271 configECHO_SERVER_ADDR3 );
272 }
273 #endif /* defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 ) */
274
275 xEchoServerAddress.sin_family = FREERTOS_AF_INET;
276
277 for( ; ; )
278 {
279 /* Create a socket. */
280 xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
281 configASSERT( xSocket != FREERTOS_INVALID_SOCKET );
282
283 /* Set a time out so a missing reply does not cause the task to block
284 * indefinitely. */
285 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );
286
287 /* Send a number of echo requests. */
288 for( lLoopCount = 0; lLoopCount < lMaxLoopCount; lLoopCount++ )
289 {
290 /* This task is going to send using the zero copy interface. The
291 * data being sent is therefore written directly into a buffer that is
292 * passed by reference into the FreeRTOS_sendto() function. First
293 * obtain a buffer of adequate size from the IP stack. Although a max
294 * delay is used, the actual delay will be capped to
295 * ipconfigMAX_SEND_BLOCK_TIME_TICKS, hence the test to ensure a buffer
296 * was actually obtained. */
297 pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xBufferLength, portMAX_DELAY );
298
299 if( pucUDPPayloadBuffer != NULL )
300 {
301 /* A buffer was successfully obtained. Create the string that is
302 * sent to the echo server. Note the string is written directly
303 * into the buffer obtained from the IP stack. */
304 sprintf( ( char * ) pucUDPPayloadBuffer, "%s %u\r\n", "Zero copy message number", ( unsigned int ) ulTxCount );
305
306 /* Also copy the string into a local buffer so it can be compared
307 * with the string that is later received back from the echo server. */
308 strcpy( cTxString, ( char * ) pucUDPPayloadBuffer );
309
310 /* Pass the buffer into the send function. ulFlags has the
311 * FREERTOS_ZERO_COPY bit set so the IP stack will take control of
312 * the buffer, rather than copy data out of the buffer. */
313 echoMARK_SEND_IN_TRACE_BUFFER( xZeroCopySendEvent );
314 lReturned = FreeRTOS_sendto( xSocket, /* The socket being sent to. */
315 ( void * ) pucUDPPayloadBuffer, /* The buffer being passed into the IP stack. */
316 strlen( cTxString ) + 1, /* The length of the data being sent. Plus 1 to ensure the null terminator is part of the data. */
317 FREERTOS_ZERO_COPY, /* ulFlags with the zero copy bit is set. */
318 &xEchoServerAddress, /* Where the data is being sent. */
319 sizeof( xEchoServerAddress ) );
320
321 if( lReturned == 0 )
322 {
323 /* The send operation failed, so this task is still
324 * responsible for the buffer obtained from the IP stack. To
325 * ensure the buffer is not lost it must either be used again,
326 * or, as in this case, returned to the IP stack using
327 * FreeRTOS_ReleaseUDPPayloadBuffer(). pucUDPPayloadBuffer can
328 * be safely re-used to receive from the socket below once the
329 * buffer has been returned to the stack. */
330 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
331 }
332 else
333 {
334 /* The send was successful so the IP stack is now managing
335 * the buffer pointed to by pucUDPPayloadBuffer, and the IP
336 * stack will return the buffer once it has been sent.
337 * pucUDPPayloadBuffer can be safely re-used to receive from
338 * the socket below. */
339 }
340
341 /* Keep a count of how many echo requests have been transmitted
342 * so it can be compared to the number of echo replies received.
343 * It would be expected to loose at least one to an ARP message the
344 * first time the connection is created. */
345 ulTxCount++;
346
347 /* Receive data on the socket. ulFlags has the zero copy bit set
348 * (FREERTOS_ZERO_COPY) indicating to the stack that a reference to
349 * the received data should be passed out to this task using the
350 * second parameter to the FreeRTOS_recvfrom() call. When this is
351 * done the IP stack is no longer responsible for releasing the
352 * buffer, and the task *must* return the buffer to the stack when
353 * it is no longer needed. By default the receive block time is
354 * portMAX_DELAY. */
355 echoMARK_SEND_IN_TRACE_BUFFER( xZeroCopyReceiveEvent );
356 lReturned = FreeRTOS_recvfrom( xSocket, /* The socket to receive from. */
357 ( void * ) &pucUDPPayloadBuffer, /* pucUDPPayloadBuffer will be set to point to the buffer that already contains the received data. */
358 0, /* Ignored because the zero copy interface is being used. */
359 FREERTOS_ZERO_COPY, /* ulFlags with the FREERTOS_ZERO_COPY bit set. */
360 &xEchoServerAddress, /* The address from which the data was sent. */
361 &xAddressLength );
362
363 if( lReturned > 0 )
364 {
365 /* Compare the string sent to the echo server with the string
366 * received back from the echo server. */
367 if( strcmp( ( char * ) pucUDPPayloadBuffer, cTxString ) == 0 )
368 {
369 /* The strings matched. */
370 ulRxCount++;
371 }
372
373 /* The buffer that contains the data passed out of the stack
374 * must* be returned to the stack. */
375 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );
376 }
377 }
378 }
379
380 /* Pause for a short while to ensure the network is not too
381 * congested. */
382 vTaskDelay( echoLOOP_DELAY );
383
384 /* Close this socket before looping back to create another. */
385 FreeRTOS_closesocket( xSocket );
386 }
387 }
388 /*-----------------------------------------------------------*/
389