]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/abstractions/platform/freertos/iot_network_freertos.c
Correct an err in queue.c introduced when previously updating behaviour when queue...
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-IoT-Libraries / abstractions / platform / freertos / iot_network_freertos.c
1 /*\r
2  * Amazon FreeRTOS Platform V1.0.0\r
3  * Copyright (C) 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\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
11  *\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
14  *\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
21  *\r
22  * http://aws.amazon.com/freertos\r
23  * http://www.FreeRTOS.org\r
24  */\r
25 \r
26 /**\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
30  */\r
31 \r
32 /* The config header is always included first. */\r
33 #include "iot_config.h"\r
34 \r
35 /* Standard includes. */\r
36 #include <string.h>\r
37 \r
38 /* FreeRTOS includes. */\r
39 #include "FreeRTOS.h"\r
40 #include "semphr.h"\r
41 #include "event_groups.h"\r
42 \r
43 /* FreeRTOS+TCP includes. */\r
44 #include "FreeRTOS_IP.h"\r
45 #include "FreeRTOS_Sockets.h"\r
46 \r
47 /* FreeRTOS-IoT-Libraries includes. */\r
48 #include "private/iot_error.h"\r
49 #include "platform/iot_network_freertos.h"\r
50 \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
54 #else\r
55     #ifdef IOT_LOG_LEVEL_GLOBAL\r
56         #define LIBRARY_LOG_LEVEL    IOT_LOG_LEVEL_GLOBAL\r
57     #else\r
58         #define LIBRARY_LOG_LEVEL    IOT_LOG_NONE\r
59     #endif\r
60 #endif\r
61 \r
62 #define LIBRARY_LOG_NAME    ( "NET" )\r
63 #include "iot_logging_setup.h"\r
64 \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
69 #endif\r
70 \r
71 /**\r
72  * @brief The event group bit to set when a connection's socket is shut down.\r
73  */\r
74 #define _SHUTDOWN_BITMASK                   ( 1UL << 0UL )\r
75 \r
76 /**\r
77  * @brief The event group bit to set when a connection's receive task exits.\r
78  */\r
79 #define _RECEIVE_TASK_EXITED_BITMASK        ( 1UL << 1UL )\r
80 \r
81 /**\r
82  * @brief The event group bit to set when the connection is destroyed from the\r
83  * receive task.\r
84  */\r
85 #define _CONNECTION_DESTROYED_BITMASK       ( 1UL << 2UL )\r
86 \r
87 /**\r
88  * @brief Maximum length of a DNS name.\r
89  *\r
90  * Per https://tools.ietf.org/html/rfc1035, 253 is the maximum string length\r
91  * of a DNS name.\r
92  */\r
93 #define _MAX_DNS_NAME_LENGTH                ( 253 )\r
94 /*-----------------------------------------------------------*/\r
95 \r
96 typedef struct _networkConnection\r
97 {\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
110 \r
111 /**\r
112  * @brief An #IotNetworkInterface_t that uses the functions in this file.\r
113  */\r
114 const IotNetworkInterface_t IotNetworkFreeRTOS =\r
115 {\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
122 };\r
123 /*-----------------------------------------------------------*/\r
124 \r
125 /**\r
126  * @brief Destroys a network connection.\r
127  *\r
128  * @param[in] pNetworkConnection The connection to destroy.\r
129  */\r
130 static void _destroyConnection( _networkConnection_t * pNetworkConnection )\r
131 {\r
132     /* Call FreeRTOS+TCP close function to free resources. */\r
133     ( void ) FreeRTOS_closesocket( pNetworkConnection->socket  );\r
134 \r
135     /* Free the network connection. */\r
136     vPortFree( pNetworkConnection );\r
137 }\r
138 /*-----------------------------------------------------------*/\r
139 \r
140 /**\r
141  * @brief Task routine that waits on incoming network data.\r
142  *\r
143  * @param[in] pArgument The network connection.\r
144  */\r
145 static void _networkReceiveTask( void * pArgument )\r
146 {\r
147     bool destroyConnection = false;\r
148     int32_t socketStatus = 0;\r
149     EventBits_t connectionEventGroupBits = 0;\r
150 \r
151     /* Cast network connection to the correct type. */\r
152     _networkConnection_t * pNetworkConnection = pArgument;\r
153 \r
154     while( true )\r
155     {\r
156         /* No buffered byte should be in the connection. */\r
157         configASSERT( pNetworkConnection->bufferedByteValid == false );\r
158 \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
162         do\r
163         {\r
164             socketStatus = FreeRTOS_recv( pNetworkConnection->socket,\r
165                                           &( pNetworkConnection->bufferedByte ),\r
166                                           1,\r
167                                           0 );\r
168 \r
169             connectionEventGroupBits = xEventGroupGetBits( pNetworkConnection->connectionEventGroup );\r
170 \r
171             if( ( connectionEventGroupBits & _SHUTDOWN_BITMASK ) == _SHUTDOWN_BITMASK )\r
172             {\r
173                 socketStatus = FREERTOS_ECLOSED;\r
174             }\r
175 \r
176             /* Check for timeout. Some ports return 0, some return EWOULDBLOCK. */\r
177         } while( ( socketStatus == 0 ) || ( socketStatus == FREERTOS_EWOULDBLOCK ) );\r
178 \r
179         if( socketStatus <= 0 )\r
180         {\r
181             break;\r
182         }\r
183 \r
184         pNetworkConnection->bufferedByteValid = true;\r
185 \r
186         /* Invoke the network callback. */\r
187         pNetworkConnection->receiveCallback( pNetworkConnection,\r
188                                              pNetworkConnection->pReceiveContext );\r
189 \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
194 \r
195         if( ( connectionEventGroupBits & _CONNECTION_DESTROYED_BITMASK ) == _CONNECTION_DESTROYED_BITMASK )\r
196         {\r
197             destroyConnection = true;\r
198             break;\r
199         }\r
200     }\r
201 \r
202     IotLogDebug( "Network receive task terminating." );\r
203 \r
204     /* If necessary, destroy the network connection before exiting. */\r
205     if( destroyConnection == true )\r
206     {\r
207         _destroyConnection( pNetworkConnection );\r
208     }\r
209     else\r
210     {\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
214     }\r
215 \r
216     vTaskDelete( NULL );\r
217 }\r
218 /*-----------------------------------------------------------*/\r
219 \r
220 IotNetworkError_t IotNetworkFreeRTOS_Create( void * pConnectionInfo,\r
221                                              void * pCredentialInfo,\r
222                                              void ** pConnection ) //_RB_ Why all void* and why a void**?\r
223 {\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
230 \r
231     /* TLS is not enabled in this version and therefore pCredentialInfo\r
232     must be NULL. */\r
233     configASSERT( pCredentialInfo == NULL );\r
234 \r
235     /* Cast function parameters to correct types. */\r
236     const IotNetworkServerInfo_t * pServerInfo = pConnectionInfo;\r
237     _networkConnection_t ** pNetworkConnection = ( _networkConnection_t ** ) pConnection;\r
238 \r
239     /* Check host name length against the maximum length allowed. */\r
240     const size_t hostnameLength = strlen( pServerInfo->pHostName );\r
241 \r
242     if( hostnameLength > ( size_t ) _MAX_DNS_NAME_LENGTH )\r
243     {\r
244         IotLogError( "Host name length exceeds %d, which is the maximum allowed.",\r
245                      _MAX_DNS_NAME_LENGTH );\r
246         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_BAD_PARAMETER );\r
247     }\r
248 \r
249     pNewNetworkConnection = pvPortMalloc( sizeof( _networkConnection_t ) );\r
250 \r
251     if( pNewNetworkConnection == NULL )\r
252     {\r
253         IotLogError( "Failed to allocate memory for new network connection." );\r
254         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_NO_MEMORY );\r
255     }\r
256 \r
257     /* Clear the connection information. */\r
258     ( void ) memset( pNewNetworkConnection, 0x00, sizeof( _networkConnection_t ) );\r
259 \r
260     /* Create a new TCP socket. */\r
261     tcpSocket = FreeRTOS_socket( FREERTOS_AF_INET,\r
262                                  FREERTOS_SOCK_STREAM,\r
263                                  FREERTOS_IPPROTO_TCP );\r
264 \r
265     if( tcpSocket == FREERTOS_INVALID_SOCKET )\r
266     {\r
267         IotLogError( "Failed to create new socket." );\r
268         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
269     }\r
270 \r
271     /* Establish connection. */\r
272     serverAddress.sin_family = FREERTOS_AF_INET;\r
273     serverAddress.sin_port = FreeRTOS_htons( pServerInfo->port );\r
274     serverAddress.sin_addr = FreeRTOS_gethostbyname( pServerInfo->pHostName );\r
275     serverAddress.sin_len = ( uint8_t ) sizeof( serverAddress );\r
276 \r
277     /* Check for errors from DNS lookup. */\r
278     if( serverAddress.sin_addr == 0 )\r
279     {\r
280         IotLogError( "Failed to resolve %s.", pServerInfo->pHostName );\r
281         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
282     }\r
283     //_RB_ Connects without setting a read block time.\r
284     socketStatus = FreeRTOS_connect( tcpSocket,\r
285                                      &serverAddress,\r
286                                      sizeof( serverAddress ) );\r
287 \r
288     if( socketStatus != 0 )\r
289     {\r
290         IotLogError( "Failed to establish new connection." );\r
291         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
292     }\r
293 \r
294     /* Set a long timeout for receive. */\r
295     socketStatus = FreeRTOS_setsockopt( tcpSocket,\r
296                                         0,\r
297                                         FREERTOS_SO_RCVTIMEO,\r
298                                         &receiveTimeout,\r
299                                         sizeof( TickType_t ) );\r
300 \r
301     if( socketStatus != 0 )\r
302     {\r
303         IotLogError( "Failed to set socket receive timeout." );\r
304         IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );\r
305     }\r
306 \r
307     IOT_FUNCTION_CLEANUP_BEGIN();\r
308 \r
309     /* Clean up on failure. */\r
310     if( status != IOT_NETWORK_SUCCESS )\r
311     {\r
312         if( tcpSocket != FREERTOS_INVALID_SOCKET )\r
313         {\r
314             FreeRTOS_closesocket( tcpSocket );\r
315         }\r
316 \r
317         /* Clear the connection information. */\r
318         if( pNewNetworkConnection != NULL )\r
319         {\r
320             vPortFree( pNewNetworkConnection );\r
321         }\r
322     }\r
323     else\r
324     {\r
325         /* Set the socket. */\r
326         pNewNetworkConnection->socket = tcpSocket;\r
327 \r
328         /* Create the connection event group and socket mutex. */\r
329         pNewNetworkConnection->connectionEventGroup = xEventGroupCreateStatic( &( pNewNetworkConnection->connectionEventGroupStorage ) );\r
330         pNewNetworkConnection->socketMutex = xSemaphoreCreateMutexStatic( &( pNewNetworkConnection->socketMutexStorage ) );\r
331 \r
332         /* Set the output parameter. */\r
333         *pNetworkConnection = pNewNetworkConnection;\r
334     }\r
335 \r
336     IOT_FUNCTION_CLEANUP_END();\r
337 }\r
338 /*-----------------------------------------------------------*/\r
339 \r
340 IotNetworkError_t IotNetworkFreeRTOS_SetReceiveCallback( void * pConnection,\r
341                                                          IotNetworkReceiveCallback_t receiveCallback,\r
342                                                          void * pContext )\r
343 {\r
344     IotNetworkError_t status = IOT_NETWORK_SUCCESS;\r
345 \r
346     /* Cast network connection to the correct type. */\r
347     _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;\r
348 \r
349     /* Set the receive callback and context. */\r
350     pNetworkConnection->receiveCallback = receiveCallback;\r
351     pNetworkConnection->pReceiveContext = pContext;\r
352 \r
353     /* No bit should be set in the connection event group. */\r
354     configASSERT( xEventGroupGetBits( pNetworkConnection->connectionEventGroup ) == 0 );\r
355 \r
356     /* Create task that waits for incoming data. */\r
357     if( xTaskCreate( _networkReceiveTask,\r
358                      "NetRecv",\r
359                      IOT_NETWORK_RECEIVE_TASK_STACK_SIZE,\r
360                      pNetworkConnection,\r
361                      IOT_NETWORK_RECEIVE_TASK_PRIORITY,\r
362                      &( pNetworkConnection->receiveTask ) ) != pdPASS )\r
363     {\r
364         IotLogError( "Failed to create network receive task." );\r
365 \r
366         status = IOT_NETWORK_SYSTEM_ERROR;\r
367     }\r
368 \r
369     return status;\r
370 }\r
371 /*-----------------------------------------------------------*/\r
372 \r
373 size_t IotNetworkFreeRTOS_Send( void * pConnection,\r
374                                 const uint8_t * pMessage,\r
375                                 size_t messageLength )\r
376 {\r
377     size_t bytesSent = 0;\r
378     int32_t socketStatus = 0;\r
379 \r
380     /* Cast network connection to the correct type. */\r
381     _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;\r
382 \r
383     /* Only one thread at a time may send on the connection. Lock the socket\r
384      * mutex to prevent other threads from sending. */\r
385     if( xSemaphoreTake( pNetworkConnection->socketMutex, portMAX_DELAY ) == pdTRUE )\r
386     {\r
387         socketStatus = FreeRTOS_send( pNetworkConnection->socket,\r
388                                       pMessage,\r
389                                       messageLength,\r
390                                       0 );\r
391 \r
392         if( socketStatus > 0 )\r
393         {\r
394             bytesSent = ( size_t ) socketStatus;\r
395         }\r
396 \r
397         xSemaphoreGive( pNetworkConnection->socketMutex );\r
398     }\r
399 \r
400     return bytesSent;\r
401 }\r
402 /*-----------------------------------------------------------*/\r
403 \r
404 size_t IotNetworkFreeRTOS_Receive( void * pConnection,\r
405                                    uint8_t * pBuffer,\r
406                                    size_t bytesRequested )\r
407 {\r
408     int32_t socketStatus = 0;\r
409     size_t bytesReceived = 0, bytesRemaining = bytesRequested;\r
410 \r
411     /* Cast network connection to the correct type. */\r
412     _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;\r
413 \r
414     /* Write the buffered byte. THIS IS A TEMPORARY WORKAROUND AND ASSUMES THIS\r
415      * FUNCTION IS ALWAYS CALLED FROM THE RECEIVE CALLBACK. */\r
416     if( pNetworkConnection->bufferedByteValid == true )\r
417     {\r
418         *pBuffer = pNetworkConnection->bufferedByte;\r
419         bytesReceived = 1;\r
420         bytesRemaining--;\r
421         pNetworkConnection->bufferedByteValid = false;\r
422     }\r
423 \r
424     /* Block and wait for incoming data. */\r
425     while( bytesRemaining > 0 )\r
426     {\r
427         socketStatus = FreeRTOS_recv( pNetworkConnection->socket,\r
428                                       pBuffer + bytesReceived,\r
429                                       bytesRemaining,\r
430                                       0 );\r
431 \r
432         if( socketStatus == FREERTOS_EWOULDBLOCK )\r
433         {\r
434             /* The return value EWOULDBLOCK means no data was received within\r
435              * the socket timeout. Ignore it and try again. */\r
436             continue;\r
437         }\r
438         else if( socketStatus <= 0 )\r
439         {\r
440             IotLogError( "Error %ld while receiving data.", ( long int ) socketStatus );\r
441             break;\r
442         }\r
443         else\r
444         {\r
445             bytesReceived += ( size_t ) socketStatus;\r
446             bytesRemaining -= ( size_t ) socketStatus;\r
447 \r
448             configASSERT( bytesReceived + bytesRemaining == bytesRequested );\r
449         }\r
450     }\r
451 \r
452     if( bytesReceived < bytesRequested )\r
453     {\r
454         IotLogWarn( "Receive requested %lu bytes, but %lu bytes received instead.",\r
455                     ( unsigned long ) bytesRequested,\r
456                     ( unsigned long ) bytesReceived );\r
457     }\r
458     else\r
459     {\r
460         IotLogDebug( "Successfully received %lu bytes.",\r
461                      ( unsigned long ) bytesRequested );\r
462     }\r
463 \r
464     return bytesReceived;\r
465 }\r
466 /*-----------------------------------------------------------*/\r
467 \r
468 IotNetworkError_t IotNetworkFreeRTOS_Close( void * pConnection )\r
469 {\r
470     int32_t socketStatus = 0;\r
471 \r
472     /* Cast network connection to the correct type. */\r
473     _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;\r
474 \r
475     /* Call socket shutdown function to close connection. */\r
476     socketStatus = FreeRTOS_shutdown( pNetworkConnection->socket,\r
477                                       FREERTOS_SHUT_RDWR );\r
478 \r
479     if( socketStatus != 0 )\r
480     {\r
481         IotLogWarn( "Failed to close connection." );\r
482     }\r
483 \r
484     /* Set the shutdown bit in the connection event group. */\r
485     ( void ) xEventGroupSetBits( pNetworkConnection->connectionEventGroup,\r
486                                  _SHUTDOWN_BITMASK );\r
487 \r
488     return IOT_NETWORK_SUCCESS;\r
489 }\r
490 /*-----------------------------------------------------------*/\r
491 \r
492 IotNetworkError_t IotNetworkFreeRTOS_Destroy( void * pConnection )\r
493 {\r
494     /* Cast network connection to the correct type. */\r
495     _networkConnection_t * pNetworkConnection = ( _networkConnection_t * ) pConnection;\r
496 \r
497     /* Check if this function is being called from the receive task. */\r
498     if( xTaskGetCurrentTaskHandle() == pNetworkConnection->receiveTask )\r
499     {\r
500         /* Set the bit specifying that the connection is destroyed. */\r
501         ( void ) xEventGroupSetBits( pNetworkConnection->connectionEventGroup,\r
502                                      _CONNECTION_DESTROYED_BITMASK );\r
503     }\r
504     else\r
505     {\r
506         /* If a receive task was created, wait for it to exit. */\r
507         if( pNetworkConnection->receiveTask != NULL )\r
508         {\r
509             ( void ) xEventGroupWaitBits( pNetworkConnection->connectionEventGroup,\r
510                                           _RECEIVE_TASK_EXITED_BITMASK,\r
511                                           pdTRUE,\r
512                                           pdTRUE,\r
513                                           portMAX_DELAY );\r
514         }\r
515 \r
516         _destroyConnection( pNetworkConnection );\r
517     }\r
518 \r
519     return IOT_NETWORK_SUCCESS;\r
520 }\r
521 /*-----------------------------------------------------------*/\r