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  *  http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
29  *  the FAQ page "My application does not run, what could be wrong?".  Have you
30  *  defined configASSERT()?
31  *
32  *  http://www.FreeRTOS.org/support - In return for receiving this top quality
33  *  embedded software for free we request you assist our global community by
34  *  participating in the support forum.
35  *
36  *  http://www.FreeRTOS.org/training - Investing in training allows your team to
37  *  be as productive as possible as early as possible.  Now you can receive
38  *  FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
39  *  Ltd, and the world's leading authority on the world's leading RTOS.
40  *
41  *  http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
42  *  including FreeRTOS+Trace - an indispensable productivity tool, a DOS
43  *  compatible FAT file system, and our tiny thread aware UDP/IP stack.
44  *
45  *  http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
46  *  Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
47  *
48  *  http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
49  *  Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS
50  *  licenses offer ticketed support, indemnification and commercial middleware.
51  *
52  *  http://www.SafeRTOS.com - High Integrity Systems also provide a safety
53  *  engineered and independently SIL3 certified version for use in safety and
54  *  mission critical applications that require provable dependability.
55  *
56  */
57 
58 /*
59  * FreeRTOS tasks are used with FreeRTOS+TCP to create a TCP echo server on the
60  * standard echo port number (7).
61  *
62  * See the following web page for essential demo usage and configuration
63  * details:
64  * https://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/TCP_Echo_Server.html
65  */
66 
67 /* Standard includes. */
68 #include <stdint.h>
69 #include <stdio.h>
70 #include <limits.h>
71 
72 /* FreeRTOS includes. */
73 #include "FreeRTOS.h"
74 #include "task.h"
75 #include "semphr.h"
76 
77 /* FreeRTOS+TCP includes. */
78 #include "FreeRTOS_IP.h"
79 #include "FreeRTOS_Sockets.h"
80 
81 /* Remove the whole file if FreeRTOSIPConfig.h is set to exclude TCP. */
82 #if ( ipconfigUSE_TCP == 1 )
83 
84 /* The maximum time to wait for a closing socket to close. */
85     #define tcpechoSHUTDOWN_DELAY    ( pdMS_TO_TICKS( 5000 ) )
86 
87 /* The standard echo port number. */
88     #define tcpechoPORT_NUMBER       7
89 
90 /* If ipconfigUSE_TCP_WIN is 1 then the Tx sockets will use a buffer size set by
91  * ipconfigTCP_TX_BUFFER_LENGTH, and the Tx window size will be
92  * configECHO_SERVER_TX_WINDOW_SIZE times the buffer size.  Note
93  * ipconfigTCP_TX_BUFFER_LENGTH is set in FreeRTOSIPConfig.h as it is a standard TCP/IP
94  * stack constant, whereas configECHO_SERVER_TX_WINDOW_SIZE is set in
95  * FreeRTOSConfig.h as it is a demo application constant. */
96     #ifndef configECHO_SERVER_TX_WINDOW_SIZE
97         #define configECHO_SERVER_TX_WINDOW_SIZE    2
98     #endif
99 
100 /* If ipconfigUSE_TCP_WIN is 1 then the Rx sockets will use a buffer size set by
101  * ipconfigTCP_RX_BUFFER_LENGTH, and the Rx window size will be
102  * configECHO_SERVER_RX_WINDOW_SIZE times the buffer size.  Note
103  * ipconfigTCP_RX_BUFFER_LENGTH is set in FreeRTOSIPConfig.h as it is a standard TCP/IP
104  * stack constant, whereas configECHO_SERVER_RX_WINDOW_SIZE is set in
105  * FreeRTOSConfig.h as it is a demo application constant. */
106     #ifndef configECHO_SERVER_RX_WINDOW_SIZE
107         #define configECHO_SERVER_RX_WINDOW_SIZE    2
108     #endif
109 
110 /*-----------------------------------------------------------*/
111 
112 /*
113  * Uses FreeRTOS+TCP to listen for incoming echo connections, creating a task
114  * to handle each connection.
115  */
116     static void prvConnectionListeningTask( void * pvParameters );
117 
118 /*
119  * Created by the connection listening task to handle a single connection.
120  */
121     static void prvServerConnectionInstance( void * pvParameters );
122 
123 /*-----------------------------------------------------------*/
124 
125 /* Stores the stack size passed into vStartSimpleTCPServerTasks() so it can be
126  * reused when the server listening task creates tasks to handle connections. */
127     static uint16_t usUsedStackSize = 0;
128 
129 /* Create task stack and buffers for use in the Listening and Server connection tasks */
130     static StaticTask_t listenerTaskBuffer;
131     static StackType_t listenerTaskStack[ PTHREAD_STACK_MIN ];
132 
133     static StaticTask_t echoServerTaskBuffer;
134     static StackType_t echoServerTaskStack[ PTHREAD_STACK_MIN ];
135 
136 /*-----------------------------------------------------------*/
137 
vStartSimpleTCPServerTasks(uint16_t usStackSize,UBaseType_t uxPriority)138     void vStartSimpleTCPServerTasks( uint16_t usStackSize,
139                                      UBaseType_t uxPriority )
140     {
141         /* Create the TCP echo server. */
142         xTaskCreateStatic( prvConnectionListeningTask,
143                            "ServerListener",
144                            PTHREAD_STACK_MIN,
145                            NULL,
146                            uxPriority + 1,
147                            listenerTaskStack,
148                            &listenerTaskBuffer );
149 
150         /* Remember the requested stack size so it can be re-used by the server
151          * listening task when it creates tasks to handle connections. */
152         usUsedStackSize = usStackSize;
153     }
154 /*-----------------------------------------------------------*/
155 
prvConnectionListeningTask(void * pvParameters)156     static void prvConnectionListeningTask( void * pvParameters )
157     {
158         struct freertos_sockaddr xClient, xBindAddress;
159         Socket_t xListeningSocket, xConnectedSocket;
160         socklen_t xSize = sizeof( xClient );
161         static const TickType_t xReceiveTimeOut = portMAX_DELAY;
162         const BaseType_t xBacklog = 20;
163 
164         #if ( ipconfigUSE_TCP_WIN == 1 )
165             WinProperties_t xWinProps;
166 
167             /* Fill in the buffer and window sizes that will be used by the socket. */
168             xWinProps.lTxBufSize = ipconfigTCP_TX_BUFFER_LENGTH;
169             xWinProps.lTxWinSize = configECHO_SERVER_TX_WINDOW_SIZE;
170             xWinProps.lRxBufSize = ipconfigTCP_RX_BUFFER_LENGTH;
171             xWinProps.lRxWinSize = configECHO_SERVER_RX_WINDOW_SIZE;
172         #endif /* ipconfigUSE_TCP_WIN */
173 
174         /* Just to prevent compiler warnings. */
175         ( void ) pvParameters;
176 
177         /* Attempt to open the socket. */
178         xListeningSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );
179         configASSERT( xListeningSocket != FREERTOS_INVALID_SOCKET );
180 
181         /* Set a time out so accept() will just wait for a connection. */
182         FreeRTOS_setsockopt( xListeningSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );
183 
184         /* Set the window and buffer sizes. */
185         #if ( ipconfigUSE_TCP_WIN == 1 )
186         {
187             FreeRTOS_setsockopt( xListeningSocket, 0, FREERTOS_SO_WIN_PROPERTIES, ( void * ) &xWinProps, sizeof( xWinProps ) );
188         }
189         #endif /* ipconfigUSE_TCP_WIN */
190 
191         /* Bind the socket to the port that the client task will send to, then
192          * listen for incoming connections. */
193         xBindAddress.sin_port = tcpechoPORT_NUMBER;
194         xBindAddress.sin_port = FreeRTOS_htons( xBindAddress.sin_port );
195         xBindAddress.sin_family = FREERTOS_AF_INET;
196         FreeRTOS_bind( xListeningSocket, &xBindAddress, sizeof( xBindAddress ) );
197         FreeRTOS_listen( xListeningSocket, xBacklog );
198 
199         for( ; ; )
200         {
201             /* Wait for a client to connect. */
202             xConnectedSocket = FreeRTOS_accept( xListeningSocket, &xClient, &xSize );
203             configASSERT( xConnectedSocket != FREERTOS_INVALID_SOCKET );
204 
205             /* Spawn a task to handle the connection. */
206             xTaskCreateStatic( prvServerConnectionInstance,
207                                "EchoServer",
208                                PTHREAD_STACK_MIN,
209                                ( void * ) xConnectedSocket,
210                                tskIDLE_PRIORITY,
211                                echoServerTaskStack,
212                                &echoServerTaskBuffer );
213         }
214     }
215 /*-----------------------------------------------------------*/
216 
prvServerConnectionInstance(void * pvParameters)217     static void prvServerConnectionInstance( void * pvParameters )
218     {
219         int32_t lBytes, lSent, lTotalSent;
220         Socket_t xConnectedSocket;
221         static const TickType_t xReceiveTimeOut = pdMS_TO_TICKS( 5000 );
222         static const TickType_t xSendTimeOut = pdMS_TO_TICKS( 5000 );
223         TickType_t xTimeOnShutdown;
224         uint8_t * pucRxBuffer;
225 
226         xConnectedSocket = ( Socket_t ) pvParameters;
227 
228         /* Attempt to create the buffer used to receive the string to be echoed
229          * back.  This could be avoided using a zero copy interface that just returned
230          * the same buffer. */
231         pucRxBuffer = ( uint8_t * ) pvPortMalloc( ipconfigTCP_MSS );
232 
233         if( pucRxBuffer != NULL )
234         {
235             FreeRTOS_setsockopt( xConnectedSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );
236             FreeRTOS_setsockopt( xConnectedSocket, 0, FREERTOS_SO_SNDTIMEO, &xSendTimeOut, sizeof( xReceiveTimeOut ) );
237 
238             for( ; ; )
239             {
240                 /* Zero out the receive array so there is NULL at the end of the string
241                  * when it is printed out. */
242                 memset( pucRxBuffer, 0x00, ipconfigTCP_MSS );
243 
244                 /* Receive data on the socket. */
245                 lBytes = FreeRTOS_recv( xConnectedSocket, pucRxBuffer, ipconfigTCP_MSS, 0 );
246 
247                 /* If data was received, echo it back. */
248                 if( lBytes >= 0 )
249                 {
250                     lSent = 0;
251                     lTotalSent = 0;
252 
253                     /* Call send() until all the data has been sent. */
254                     while( ( lSent >= 0 ) && ( lTotalSent < lBytes ) )
255                     {
256                         lSent = FreeRTOS_send( xConnectedSocket, pucRxBuffer, lBytes - lTotalSent, 0 );
257                         lTotalSent += lSent;
258                     }
259 
260                     if( lSent < 0 )
261                     {
262                         /* Socket closed? */
263                         break;
264                     }
265                 }
266                 else
267                 {
268                     /* Socket closed? */
269                     break;
270                 }
271             }
272         }
273 
274         /* Initiate a shutdown in case it has not already been initiated. */
275         FreeRTOS_shutdown( xConnectedSocket, FREERTOS_SHUT_RDWR );
276 
277         /* Wait for the shutdown to take effect, indicated by FreeRTOS_recv()
278          * returning an error. */
279         xTimeOnShutdown = xTaskGetTickCount();
280 
281         do
282         {
283             if( FreeRTOS_recv( xConnectedSocket, pucRxBuffer, ipconfigTCP_MSS, 0 ) < 0 )
284             {
285                 break;
286             }
287         } while( ( xTaskGetTickCount() - xTimeOnShutdown ) < tcpechoSHUTDOWN_DELAY );
288 
289         /* Finished with the socket, buffer, the task. */
290         vPortFree( pucRxBuffer );
291         FreeRTOS_closesocket( xConnectedSocket );
292 
293         vTaskDelete( NULL );
294     }
295 /*-----------------------------------------------------------*/
296 
297 /* The whole file is excluded if TCP is not compiled in. */
298 #endif /* ipconfigUSE_TCP */
299