2 * Amazon FreeRTOS Platform V1.0.0
\r
3 * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
22 * http://aws.amazon.com/freertos
\r
23 * http://www.FreeRTOS.org
\r
27 * @file iot_network_freertos.c
\r
28 * @brief Implementation of the network-related functions from iot_network_freertos.h
\r
29 * for FreeRTOS+TCP sockets.
\r
32 /* The config header is always included first. */
\r
33 #include "iot_config.h"
\r
35 /* Standard includes. */
\r
38 /* FreeRTOS includes. */
\r
39 #include "FreeRTOS.h"
\r
41 #include "event_groups.h"
\r
43 /* FreeRTOS+TCP includes. */
\r
44 #include "FreeRTOS_IP.h"
\r
45 #include "FreeRTOS_Sockets.h"
\r
47 /* FreeRTOS-IoT-Libraries includes. */
\r
48 #include "private/iot_error.h"
\r
49 #include "platform/iot_network_freertos.h"
\r
51 /* Configure logs for the functions in this file. */
\r
52 #ifdef IOT_LOG_LEVEL_NETWORK
\r
53 #define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_NETWORK
\r
55 #ifdef IOT_LOG_LEVEL_GLOBAL
\r
56 #define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
\r
58 #define LIBRARY_LOG_LEVEL IOT_LOG_NONE
\r
62 #define LIBRARY_LOG_NAME ( "NET" )
\r
63 #include "iot_logging_setup.h"
\r
65 /* Provide a default value for the number of milliseconds for a socket poll.
\r
66 * This is a temporary workaround to deal with the lack of poll(). */
\r
67 #ifndef IOT_NETWORK_SOCKET_POLL_MS
\r
68 #define IOT_NETWORK_SOCKET_POLL_MS ( 1000 )
\r
72 * @brief The event group bit to set when a connection's socket is shut down.
\r
74 #define _SHUTDOWN_BITMASK ( 1UL << 0UL )
\r
77 * @brief The event group bit to set when a connection's receive task exits.
\r
79 #define _RECEIVE_TASK_EXITED_BITMASK ( 1UL << 1UL )
\r
82 * @brief The event group bit to set when the connection is destroyed from the
\r
85 #define _CONNECTION_DESTROYED_BITMASK ( 1UL << 2UL )
\r
88 * @brief Maximum length of a DNS name.
\r
90 * Per https://tools.ietf.org/html/rfc1035, 253 is the maximum string length
\r
93 #define _MAX_DNS_NAME_LENGTH ( 253 )
\r
94 /*-----------------------------------------------------------*/
\r
96 typedef struct _networkConnection
\r
98 Socket_t socket; /**< @brief FreeRTOS+TCP sockets handle. */
\r
99 SemaphoreHandle_t socketMutex; /**< @brief Prevents concurrent threads from sending on a socket. */
\r
100 StaticSemaphore_t socketMutexStorage; /**< @brief Storage space for socketMutex. */
\r
101 EventGroupHandle_t connectionEventGroup; /**< @brief Synchronizes with the receive task. */
\r
102 StaticEventGroup_t connectionEventGroupStorage; /**< @brief Storage space for connectionEventGroup. */
\r
103 TaskHandle_t receiveTask; /**< @brief Handle of the receive task, if any. */
\r
104 IotNetworkReceiveCallback_t receiveCallback; /**< @brief Network receive callback, if any. */
\r
105 void * pReceiveContext; /**< @brief The context for the receive callback. */
\r
106 bool bufferedByteValid; /**< @brief Used to determine if the buffered byte is valid. */
\r
107 uint8_t bufferedByte; /**< @brief A single byte buffered from a receive, since FreeRTOS+TCP sockets does not have poll(). */
\r
108 } _networkConnection_t;
\r
109 /*-----------------------------------------------------------*/
\r
112 * @brief An #IotNetworkInterface_t that uses the functions in this file.
\r
114 const IotNetworkInterface_t IotNetworkFreeRTOS =
\r
116 .create = IotNetworkFreeRTOS_Create,
\r
117 .setReceiveCallback = IotNetworkFreeRTOS_SetReceiveCallback,
\r
118 .send = IotNetworkFreeRTOS_Send,
\r
119 .receive = IotNetworkFreeRTOS_Receive,
\r
120 .close = IotNetworkFreeRTOS_Close,
\r
121 .destroy = IotNetworkFreeRTOS_Destroy
\r
123 /*-----------------------------------------------------------*/
\r
126 * @brief Destroys a network connection.
\r
128 * @param[in] pNetworkConnection The connection to destroy.
\r
130 static void _destroyConnection( _networkConnection_t * pNetworkConnection )
\r
132 /* Call FreeRTOS+TCP close function to free resources. */
\r
133 ( void ) FreeRTOS_closesocket( pNetworkConnection->socket );
\r
135 /* Free the network connection. */
\r
136 vPortFree( pNetworkConnection );
\r
138 /*-----------------------------------------------------------*/
\r
141 * @brief Task routine that waits on incoming network data.
\r
143 * @param[in] pArgument The network connection.
\r
145 static void _networkReceiveTask( void * pArgument )
\r
147 bool destroyConnection = false;
\r
148 int32_t socketStatus = 0;
\r
149 EventBits_t connectionEventGroupBits = 0;
\r
151 /* Cast network connection to the correct type. */
\r
152 _networkConnection_t * pNetworkConnection = pArgument;
\r
156 /* No buffered byte should be in the connection. */
\r
157 configASSERT( pNetworkConnection->bufferedByteValid == false );
\r
159 /* Block and wait for 1 byte of data. This simulates the behavior of poll().
\r
160 * THIS IS A TEMPORARY WORKAROUND AND DOES NOT PROVIDE THREAD-SAFETY AGAINST
\r
161 * MULTIPLE CALLS OF RECEIVE. */
\r
164 socketStatus = FreeRTOS_recv( pNetworkConnection->socket,
\r
165 &( pNetworkConnection->bufferedByte ),
\r
169 connectionEventGroupBits = xEventGroupGetBits( pNetworkConnection->connectionEventGroup );
\r
171 if( ( connectionEventGroupBits & _SHUTDOWN_BITMASK ) == _SHUTDOWN_BITMASK )
\r
173 socketStatus = FREERTOS_ECLOSED;
\r
176 /* Check for timeout. Some ports return 0, some return EWOULDBLOCK. */
\r
177 } while( ( socketStatus == 0 ) || ( socketStatus == FREERTOS_EWOULDBLOCK ) );
\r
179 if( socketStatus <= 0 )
\r
184 pNetworkConnection->bufferedByteValid = true;
\r
186 /* Invoke the network callback. */
\r
187 pNetworkConnection->receiveCallback( pNetworkConnection,
\r
188 pNetworkConnection->pReceiveContext );
\r
190 /* Check if the connection was destroyed by the receive callback. This
\r
191 * does not need to be thread-safe because the destroy connection function
\r
192 * may only be called once (per its API doc). */
\r
193 connectionEventGroupBits = xEventGroupGetBits( pNetworkConnection->connectionEventGroup );
\r
195 if( ( connectionEventGroupBits & _CONNECTION_DESTROYED_BITMASK ) == _CONNECTION_DESTROYED_BITMASK )
\r
197 destroyConnection = true;
\r
202 IotLogDebug( "Network receive task terminating." );
\r
204 /* If necessary, destroy the network connection before exiting. */
\r
205 if( destroyConnection == true )
\r
207 _destroyConnection( pNetworkConnection );
\r
211 /* Set the bit to indicate that the receive task has exited. */
\r
212 ( void ) xEventGroupSetBits( pNetworkConnection->connectionEventGroup,
\r
213 _RECEIVE_TASK_EXITED_BITMASK );
\r
216 vTaskDelete( NULL );
\r
218 /*-----------------------------------------------------------*/
\r
220 IotNetworkError_t IotNetworkFreeRTOS_Create( void * pConnectionInfo,
\r
221 void * pCredentialInfo,
\r
222 void ** pConnection )
\r
224 IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );
\r
225 Socket_t tcpSocket = FREERTOS_INVALID_SOCKET;
\r
226 int32_t socketStatus = 0;
\r
227 struct freertos_sockaddr serverAddress = { 0 };
\r
228 const TickType_t receiveTimeout = pdMS_TO_TICKS( IOT_NETWORK_SOCKET_POLL_MS );
\r
229 _networkConnection_t * pNewNetworkConnection = NULL;
\r
231 /* TLS is not supported yet and therefore pCredentialInfo must be NULL. */
\r
232 configASSERT( pCredentialInfo == NULL );
\r
234 /* Cast function parameters to correct types. */
\r
235 const IotNetworkServerInfo_t * pServerInfo = pConnectionInfo;
\r
236 _networkConnection_t ** pNetworkConnection = ( _networkConnection_t ** ) pConnection;
\r
238 /* Check host name length against the maximum length allowed. */
\r
239 const size_t hostnameLength = strlen( pServerInfo->pHostName );
\r
241 if( hostnameLength > ( size_t ) _MAX_DNS_NAME_LENGTH )
\r
243 IotLogError( "Host name length exceeds %d, which is the maximum allowed.",
\r
244 _MAX_DNS_NAME_LENGTH );
\r
245 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_BAD_PARAMETER );
\r
248 pNewNetworkConnection = pvPortMalloc( sizeof( _networkConnection_t ) );
\r
250 if( pNewNetworkConnection == NULL )
\r
252 IotLogError( "Failed to allocate memory for new network connection." );
\r
253 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_NO_MEMORY );
\r
256 /* Clear the connection information. */
\r
257 ( void ) memset( pNewNetworkConnection, 0x00, sizeof( _networkConnection_t ) );
\r
259 /* Create a new TCP socket. */
\r
260 tcpSocket = FreeRTOS_socket( FREERTOS_AF_INET,
\r
261 FREERTOS_SOCK_STREAM,
\r
262 FREERTOS_IPPROTO_TCP );
\r
264 if( tcpSocket == FREERTOS_INVALID_SOCKET )
\r
266 IotLogError( "Failed to create new socket." );
\r
267 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
270 /* Establish connection. */
\r
271 serverAddress.sin_family = FREERTOS_AF_INET;
\r
272 serverAddress.sin_port = FreeRTOS_htons( pServerInfo->port );
\r
273 serverAddress.sin_addr = FreeRTOS_gethostbyname( pServerInfo->pHostName );
\r
274 serverAddress.sin_len = ( uint8_t ) sizeof( serverAddress );
\r
276 /* Check for errors from DNS lookup. */
\r
277 if( serverAddress.sin_addr == 0 )
\r
279 IotLogError( "Failed to resolve %s.", pServerInfo->pHostName );
\r
280 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
283 socketStatus = FreeRTOS_connect( tcpSocket,
\r
285 sizeof( serverAddress ) );
\r
287 if( socketStatus != 0 )
\r
289 IotLogError( "Failed to establish new connection." );
\r
290 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
293 /* Set a long timeout for receive. */
\r
294 socketStatus = FreeRTOS_setsockopt( tcpSocket,
\r
296 FREERTOS_SO_RCVTIMEO,
\r
298 sizeof( TickType_t ) );
\r
300 if( socketStatus != 0 )
\r
302 IotLogError( "Failed to set socket receive timeout." );
\r
303 IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
\r
306 IOT_FUNCTION_CLEANUP_BEGIN();
\r
308 /* Clean up on failure. */
\r
309 if( status != IOT_NETWORK_SUCCESS )
\r
311 if( tcpSocket != FREERTOS_INVALID_SOCKET )
\r
313 FreeRTOS_closesocket( tcpSocket );
\r
316 /* Clear the connection information. */
\r
317 if( pNewNetworkConnection != NULL )
\r
319 vPortFree( pNewNetworkConnection );
\r
324 /* Set the socket. */
\r
325 pNewNetworkConnection->socket = tcpSocket;
\r
327 /* Create the connection event group and socket mutex. */
\r
328 pNewNetworkConnection->connectionEventGroup = xEventGroupCreateStatic( &( pNewNetworkConnection->connectionEventGroupStorage ) );
\r
329 pNewNetworkConnection->socketMutex = xSemaphoreCreateMutexStatic( &( pNewNetworkConnection->socketMutexStorage ) );
\r
331 /* Set the output parameter. */
\r
332 *pNetworkConnection = pNewNetworkConnection;
\r
335 IOT_FUNCTION_CLEANUP_END();
\r
337 /*-----------------------------------------------------------*/
\r
339 IotNetworkError_t IotNetworkFreeRTOS_SetReceiveCallback( void * pConnection,
\r
340 IotNetworkReceiveCallback_t receiveCallback,
\r
343 IotNetworkError_t status = IOT_NETWORK_SUCCESS;
\r
345 /* Cast network connection to the correct type. */
\r
346 _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;
\r
348 /* Set the receive callback and context. */
\r
349 pNetworkConnection->receiveCallback = receiveCallback;
\r
350 pNetworkConnection->pReceiveContext = pContext;
\r
352 /* No bit should be set in the connection event group. */
\r
353 configASSERT( xEventGroupGetBits( pNetworkConnection->connectionEventGroup ) == 0 );
\r
355 /* Create task that waits for incoming data. */
\r
356 if( xTaskCreate( _networkReceiveTask,
\r
358 IOT_NETWORK_RECEIVE_TASK_STACK_SIZE,
\r
359 pNetworkConnection,
\r
360 IOT_NETWORK_RECEIVE_TASK_PRIORITY,
\r
361 &( pNetworkConnection->receiveTask ) ) != pdPASS )
\r
363 IotLogError( "Failed to create network receive task." );
\r
365 status = IOT_NETWORK_SYSTEM_ERROR;
\r
370 /*-----------------------------------------------------------*/
\r
372 size_t IotNetworkFreeRTOS_Send( void * pConnection,
\r
373 const uint8_t * pMessage,
\r
374 size_t messageLength )
\r
376 size_t bytesSent = 0;
\r
377 int32_t socketStatus = 0;
\r
379 /* Cast network connection to the correct type. */
\r
380 _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;
\r
382 /* Only one thread at a time may send on the connection. Lock the socket
\r
383 * mutex to prevent other threads from sending. */
\r
384 if( xSemaphoreTake( pNetworkConnection->socketMutex, portMAX_DELAY ) == pdTRUE )
\r
386 socketStatus = FreeRTOS_send( pNetworkConnection->socket,
\r
391 if( socketStatus > 0 )
\r
393 bytesSent = ( size_t ) socketStatus;
\r
396 xSemaphoreGive( pNetworkConnection->socketMutex );
\r
401 /*-----------------------------------------------------------*/
\r
403 size_t IotNetworkFreeRTOS_Receive( void * pConnection,
\r
405 size_t bytesRequested )
\r
407 int32_t socketStatus = 0;
\r
408 size_t bytesReceived = 0, bytesRemaining = bytesRequested;
\r
410 /* Cast network connection to the correct type. */
\r
411 _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;
\r
413 /* Write the buffered byte. THIS IS A TEMPORARY WORKAROUND AND ASSUMES THIS
\r
414 * FUNCTION IS ALWAYS CALLED FROM THE RECEIVE CALLBACK. */
\r
415 if( pNetworkConnection->bufferedByteValid == true )
\r
417 *pBuffer = pNetworkConnection->bufferedByte;
\r
420 pNetworkConnection->bufferedByteValid = false;
\r
423 /* Block and wait for incoming data. */
\r
424 while( bytesRemaining > 0 )
\r
426 socketStatus = FreeRTOS_recv( pNetworkConnection->socket,
\r
427 pBuffer + bytesReceived,
\r
431 if( socketStatus == FREERTOS_EWOULDBLOCK )
\r
433 /* The return value EWOULDBLOCK means no data was received within
\r
434 * the socket timeout. Ignore it and try again. */
\r
437 else if( socketStatus <= 0 )
\r
439 IotLogError( "Error %ld while receiving data.", ( long int ) socketStatus );
\r
444 bytesReceived += ( size_t ) socketStatus;
\r
445 bytesRemaining -= ( size_t ) socketStatus;
\r
447 configASSERT( bytesReceived + bytesRemaining == bytesRequested );
\r
451 if( bytesReceived < bytesRequested )
\r
453 IotLogWarn( "Receive requested %lu bytes, but %lu bytes received instead.",
\r
454 ( unsigned long ) bytesRequested,
\r
455 ( unsigned long ) bytesReceived );
\r
459 IotLogDebug( "Successfully received %lu bytes.",
\r
460 ( unsigned long ) bytesRequested );
\r
463 return bytesReceived;
\r
465 /*-----------------------------------------------------------*/
\r
467 IotNetworkError_t IotNetworkFreeRTOS_Close( void * pConnection )
\r
469 int32_t socketStatus = 0;
\r
471 /* Cast network connection to the correct type. */
\r
472 _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;
\r
474 /* Call socket shutdown function to close connection. */
\r
475 socketStatus = FreeRTOS_shutdown( pNetworkConnection->socket,
\r
476 FREERTOS_SHUT_RDWR );
\r
478 if( socketStatus != 0 )
\r
480 IotLogWarn( "Failed to close connection." );
\r
483 /* Set the shutdown bit in the connection event group. */
\r
484 ( void ) xEventGroupSetBits( pNetworkConnection->connectionEventGroup,
\r
485 _SHUTDOWN_BITMASK );
\r
487 return IOT_NETWORK_SUCCESS;
\r
489 /*-----------------------------------------------------------*/
\r
491 IotNetworkError_t IotNetworkFreeRTOS_Destroy( void * pConnection )
\r
493 /* Cast network connection to the correct type. */
\r
494 _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;
\r
496 /* Check if this function is being called from the receive task. */
\r
497 if( xTaskGetCurrentTaskHandle() == pNetworkConnection->receiveTask )
\r
499 /* Set the bit specifying that the connection is destroyed. */
\r
500 ( void ) xEventGroupSetBits( pNetworkConnection->connectionEventGroup,
\r
501 _CONNECTION_DESTROYED_BITMASK );
\r
505 /* If a receive task was created, wait for it to exit. */
\r
506 if( pNetworkConnection->receiveTask != NULL )
\r
508 ( void ) xEventGroupWaitBits( pNetworkConnection->connectionEventGroup,
\r
509 _RECEIVE_TASK_EXITED_BITMASK,
\r
515 _destroyConnection( pNetworkConnection );
\r
518 return IOT_NETWORK_SUCCESS;
\r
520 /*-----------------------------------------------------------*/
\r