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