--- /dev/null
+/*\r
+ * Amazon FreeRTOS Common V1.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_logging.c\r
+ * @brief Implementation of logging functions from iot_logging.h\r
+ */\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Standard includes. */\r
+#include <stdarg.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+\r
+/* Platform clock include. */\r
+#include "platform/iot_clock.h"\r
+\r
+/* Logging includes. */\r
+#include "private/iot_logging.h"\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/* This implementation assumes the following values for the log level constants.\r
+ * Ensure that the values have not been modified. */\r
+#if IOT_LOG_NONE != 0\r
+ #error "IOT_LOG_NONE must be 0."\r
+#endif\r
+#if IOT_LOG_ERROR != 1\r
+ #error "IOT_LOG_ERROR must be 1."\r
+#endif\r
+#if IOT_LOG_WARN != 2\r
+ #error "IOT_LOG_WARN must be 2."\r
+#endif\r
+#if IOT_LOG_INFO != 3\r
+ #error "IOT_LOG_INFO must be 3."\r
+#endif\r
+#if IOT_LOG_DEBUG != 4\r
+ #error "IOT_LOG_DEBUG must be 4."\r
+#endif\r
+\r
+/**\r
+ * @def IotLogging_Puts( message )\r
+ * @brief Function the logging library uses to print a line.\r
+ *\r
+ * This function can be set by using a define. By default, the standard library\r
+ * [puts](http://pubs.opengroup.org/onlinepubs/9699919799/functions/puts.html)\r
+ * function is used.\r
+ */\r
+#ifndef IotLogging_Puts\r
+ #define IotLogging_Puts puts\r
+#endif\r
+\r
+/*\r
+ * Provide default values for undefined memory allocation functions based on\r
+ * the usage of dynamic memory allocation.\r
+ */\r
+#if IOT_STATIC_MEMORY_ONLY == 1\r
+ /* Static memory allocation header. */\r
+ #include "private/iot_static_memory.h"\r
+\r
+/**\r
+ * @brief Allocate a new logging buffer. This function must have the same\r
+ * signature as [malloc](http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).\r
+ */\r
+ #ifndef IotLogging_Malloc\r
+ #define IotLogging_Malloc Iot_MallocMessageBuffer\r
+ #endif\r
+\r
+/**\r
+ * @brief Free a logging buffer. This function must have the same signature\r
+ * as [free](http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).\r
+ */\r
+ #ifndef IotLogging_Free\r
+ #define IotLogging_Free Iot_FreeMessageBuffer\r
+ #endif\r
+\r
+/**\r
+ * @brief Get the size of a logging buffer. Statically-allocated buffers\r
+ * should all have the same size.\r
+ */\r
+ #ifndef IotLogging_StaticBufferSize\r
+ #define IotLogging_StaticBufferSize Iot_MessageBufferSize\r
+ #endif\r
+#else /* if IOT_STATIC_MEMORY_ONLY == 1 */\r
+ #ifndef IotLogging_Malloc\r
+ #include <stdlib.h>\r
+ #define IotLogging_Malloc malloc\r
+ #endif\r
+\r
+ #ifndef IotLogging_Free\r
+ #include <stdlib.h>\r
+ #define IotLogging_Free free\r
+ #endif\r
+#endif /* if IOT_STATIC_MEMORY_ONLY == 1 */\r
+\r
+/**\r
+ * @brief A guess of the maximum length of a timestring.\r
+ *\r
+ * There's no way for this logging library to know the length of a timestring\r
+ * before it's generated. Therefore, the logging library will assume a maximum\r
+ * length of any timestring it may get. This value should be generous enough\r
+ * to accommodate the vast majority of timestrings.\r
+ *\r
+ * @see @ref platform_clock_function_gettimestring\r
+ */\r
+#define MAX_TIMESTRING_LENGTH ( 64 )\r
+\r
+/**\r
+ * @brief The longest string in #_pLogLevelStrings (below), plus 3 to accommodate\r
+ * `[]` and a null-terminator.\r
+ */\r
+#define MAX_LOG_LEVEL_LENGTH ( 8 )\r
+\r
+/**\r
+ * @brief How many bytes @ref logging_function_genericprintbuffer should output on\r
+ * each line.\r
+ */\r
+#define BYTES_PER_LINE ( 16 )\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Lookup table for log levels.\r
+ *\r
+ * Converts one of the @ref logging_constants_levels to a string.\r
+ */\r
+static const char * const _pLogLevelStrings[ 5 ] =\r
+{\r
+ "", /* IOT_LOG_NONE */\r
+ "ERROR", /* IOT_LOG_ERROR */\r
+ "WARN ", /* IOT_LOG_WARN */\r
+ "INFO ", /* IOT_LOG_INFO */\r
+ "DEBUG" /* IOT_LOG_DEBUG */\r
+};\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+#if !defined( IOT_STATIC_MEMORY_ONLY ) || ( IOT_STATIC_MEMORY_ONLY == 0 )\r
+ static bool _reallocLoggingBuffer( void ** pOldBuffer,\r
+ size_t newSize,\r
+ size_t oldSize )\r
+ {\r
+ bool status = false;\r
+\r
+ /* Allocate a new, larger buffer. */\r
+ void * pNewBuffer = IotLogging_Malloc( newSize );\r
+\r
+ /* Ensure that memory allocation succeeded. */\r
+ if( pNewBuffer != NULL )\r
+ {\r
+ /* Copy the data from the old buffer to the new buffer. */\r
+ ( void ) memcpy( pNewBuffer, *pOldBuffer, oldSize );\r
+\r
+ /* Free the old buffer and update the pointer. */\r
+ IotLogging_Free( *pOldBuffer );\r
+ *pOldBuffer = pNewBuffer;\r
+\r
+ status = true;\r
+ }\r
+\r
+ return status;\r
+ }\r
+#endif /* if !defined( IOT_STATIC_MEMORY_ONLY ) || ( IOT_STATIC_MEMORY_ONLY == 0 ) */\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void IotLog_Generic( int libraryLogSetting,\r
+ const char * const pLibraryName,\r
+ int messageLevel,\r
+ const IotLogConfig_t * const pLogConfig,\r
+ const char * const pFormat,\r
+ ... )\r
+{\r
+ int requiredMessageSize = 0;\r
+ size_t bufferSize = 0,\r
+ bufferPosition = 0, timestringLength = 0;\r
+ char * pLoggingBuffer = NULL;\r
+ va_list args;\r
+\r
+ /* If the library's log level setting is lower than the message level,\r
+ * return without doing anything. */\r
+ if( ( messageLevel == 0 ) || ( messageLevel > libraryLogSetting ) )\r
+ {\r
+ return;\r
+ }\r
+\r
+ if( ( pLogConfig == NULL ) || ( pLogConfig->hideLogLevel == false ) )\r
+ {\r
+ /* Add length of log level if requested. */\r
+ bufferSize += MAX_LOG_LEVEL_LENGTH;\r
+ }\r
+\r
+ /* Estimate the amount of buffer needed for this log message. */\r
+ if( ( pLogConfig == NULL ) || ( pLogConfig->hideLibraryName == false ) )\r
+ {\r
+ /* Add size of library name if requested. Add 2 to accommodate "[]". */\r
+ bufferSize += strlen( pLibraryName ) + 2;\r
+ }\r
+\r
+ if( ( pLogConfig == NULL ) || ( pLogConfig->hideTimestring == false ) )\r
+ {\r
+ /* Add length of timestring if requested. */\r
+ bufferSize += MAX_TIMESTRING_LENGTH;\r
+ }\r
+\r
+ /* Add 64 as an initial (arbitrary) guess for the length of the message. */\r
+ bufferSize += 64;\r
+\r
+ /* In static memory mode, check that the log message will fit in the a\r
+ * static buffer. */\r
+ #if IOT_STATIC_MEMORY_ONLY == 1\r
+ if( bufferSize >= IotLogging_StaticBufferSize() )\r
+ {\r
+ /* If the static buffers are likely too small to fit the log message,\r
+ * return. */\r
+ return;\r
+ }\r
+\r
+ /* Otherwise, update the buffer size to the size of a static buffer. */\r
+ bufferSize = IotLogging_StaticBufferSize();\r
+ #endif\r
+\r
+ /* Allocate memory for the logging buffer. */\r
+ pLoggingBuffer = ( char * ) IotLogging_Malloc( bufferSize );\r
+\r
+ if( pLoggingBuffer == NULL )\r
+ {\r
+ return;\r
+ }\r
+\r
+ /* Print the message log level if requested. */\r
+ if( ( pLogConfig == NULL ) || ( pLogConfig->hideLogLevel == false ) )\r
+ {\r
+ /* Ensure that message level is valid. */\r
+ if( ( messageLevel >= IOT_LOG_NONE ) && ( messageLevel <= IOT_LOG_DEBUG ) )\r
+ {\r
+ /* Add the log level string to the logging buffer. */\r
+ requiredMessageSize = snprintf( pLoggingBuffer + bufferPosition,\r
+ bufferSize - bufferPosition,\r
+ "[%s]",\r
+ _pLogLevelStrings[ messageLevel ] );\r
+\r
+ /* Check for encoding errors. */\r
+ if( requiredMessageSize <= 0 )\r
+ {\r
+ IotLogging_Free( pLoggingBuffer );\r
+\r
+ return;\r
+ }\r
+\r
+ /* Update the buffer position. */\r
+ bufferPosition += ( size_t ) requiredMessageSize;\r
+ }\r
+ }\r
+\r
+ /* Print the library name if requested. */\r
+ if( ( pLogConfig == NULL ) || ( pLogConfig->hideLibraryName == false ) )\r
+ {\r
+ /* Add the library name to the logging buffer. */\r
+ requiredMessageSize = snprintf( pLoggingBuffer + bufferPosition,\r
+ bufferSize - bufferPosition,\r
+ "[%s]",\r
+ pLibraryName );\r
+\r
+ /* Check for encoding errors. */\r
+ if( requiredMessageSize <= 0 )\r
+ {\r
+ IotLogging_Free( pLoggingBuffer );\r
+\r
+ return;\r
+ }\r
+\r
+ /* Update the buffer position. */\r
+ bufferPosition += ( size_t ) requiredMessageSize;\r
+ }\r
+\r
+ /* Print the timestring if requested. */\r
+ if( ( pLogConfig == NULL ) || ( pLogConfig->hideTimestring == false ) )\r
+ {\r
+ /* Add the opening '[' enclosing the timestring. */\r
+ pLoggingBuffer[ bufferPosition ] = '[';\r
+ bufferPosition++;\r
+\r
+ /* Generate the timestring and add it to the buffer. */\r
+ if( IotClock_GetTimestring( pLoggingBuffer + bufferPosition,\r
+ bufferSize - bufferPosition,\r
+ ×tringLength ) == true )\r
+ {\r
+ /* If the timestring was successfully generated, add the closing "]". */\r
+ bufferPosition += timestringLength;\r
+ pLoggingBuffer[ bufferPosition ] = ']';\r
+ bufferPosition++;\r
+ }\r
+ else\r
+ {\r
+ /* Sufficient memory for a timestring should have been allocated. A timestring\r
+ * probably failed to generate due to a clock read error; remove the opening '['\r
+ * from the logging buffer. */\r
+ bufferPosition--;\r
+ pLoggingBuffer[ bufferPosition ] = '\0';\r
+ }\r
+ }\r
+\r
+ /* Add a padding space between the last closing ']' and the message, unless\r
+ * the logging buffer is empty. */\r
+ if( bufferPosition > 0 )\r
+ {\r
+ pLoggingBuffer[ bufferPosition ] = ' ';\r
+ bufferPosition++;\r
+ }\r
+\r
+ va_start( args, pFormat );\r
+\r
+ /* Add the log message to the logging buffer. */\r
+ requiredMessageSize = vsnprintf( pLoggingBuffer + bufferPosition,\r
+ bufferSize - bufferPosition,\r
+ pFormat,\r
+ args );\r
+\r
+ va_end( args );\r
+\r
+ /* If the logging buffer was too small to fit the log message, reallocate\r
+ * a larger logging buffer. */\r
+ if( ( size_t ) requiredMessageSize >= bufferSize - bufferPosition )\r
+ {\r
+ #if IOT_STATIC_MEMORY_ONLY == 1\r
+\r
+ /* There's no point trying to allocate a larger static buffer. Return\r
+ * immediately. */\r
+ IotLogging_Free( pLoggingBuffer );\r
+\r
+ return;\r
+ #else\r
+ if( _reallocLoggingBuffer( ( void ** ) &pLoggingBuffer,\r
+ ( size_t ) requiredMessageSize + bufferPosition + 1,\r
+ bufferSize ) == false )\r
+ {\r
+ /* If buffer reallocation failed, return. */\r
+ IotLogging_Free( pLoggingBuffer );\r
+\r
+ return;\r
+ }\r
+\r
+ /* Reallocation successful, update buffer size. */\r
+ bufferSize = ( size_t ) requiredMessageSize + bufferPosition + 1;\r
+\r
+ /* Add the log message to the buffer. Now that the buffer has been\r
+ * reallocated, this should succeed. */\r
+ va_start( args, pFormat );\r
+ requiredMessageSize = vsnprintf( pLoggingBuffer + bufferPosition,\r
+ bufferSize - bufferPosition,\r
+ pFormat,\r
+ args );\r
+ va_end( args );\r
+ #endif /* if IOT_STATIC_MEMORY_ONLY == 1 */\r
+ }\r
+\r
+ /* Check for encoding errors. */\r
+ if( requiredMessageSize <= 0 )\r
+ {\r
+ IotLogging_Free( pLoggingBuffer );\r
+\r
+ return;\r
+ }\r
+\r
+ /* Print the logging buffer to stdout. */\r
+ IotLogging_Puts( pLoggingBuffer );\r
+\r
+ /* Free the logging buffer. */\r
+ IotLogging_Free( pLoggingBuffer );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void IotLog_GenericPrintBuffer( const char * const pLibraryName,\r
+ const char * const pHeader,\r
+ const uint8_t * const pBuffer,\r
+ size_t bufferSize )\r
+{\r
+ size_t i = 0, offset = 0;\r
+\r
+ /* Allocate memory to hold each line of the log message. Since each byte\r
+ * of pBuffer is printed in 4 characters (2 digits, a space, and a null-\r
+ * terminator), the size of each line is 4 * BYTES_PER_LINE. */\r
+ char * pMessageBuffer = IotLogging_Malloc( 4 * BYTES_PER_LINE );\r
+\r
+ /* Exit if no memory is available. */\r
+ if( pMessageBuffer == NULL )\r
+ {\r
+ return;\r
+ }\r
+\r
+ /* Print pHeader before printing pBuffer. */\r
+ if( pHeader != NULL )\r
+ {\r
+ IotLog_Generic( IOT_LOG_DEBUG,\r
+ pLibraryName,\r
+ IOT_LOG_DEBUG,\r
+ NULL,\r
+ pHeader );\r
+ }\r
+\r
+ /* Print each byte in pBuffer. */\r
+ for( i = 0; i < bufferSize; i++ )\r
+ {\r
+ /* Print a line if BYTES_PER_LINE is reached. But don't print a line\r
+ * at the beginning (when i=0). */\r
+ if( ( i % BYTES_PER_LINE == 0 ) && ( i != 0 ) )\r
+ {\r
+ IotLogging_Puts( pMessageBuffer );\r
+\r
+ /* Reset offset so that pMessageBuffer is filled from the beginning. */\r
+ offset = 0;\r
+ }\r
+\r
+ /* Print a single byte into pMessageBuffer. */\r
+ ( void ) snprintf( pMessageBuffer + offset, 4, "%02x ", pBuffer[ i ] );\r
+\r
+ /* Move the offset where the next character is printed. */\r
+ offset += 3;\r
+ }\r
+\r
+ /* Print the final line of bytes. This line isn't printed by the for-loop above. */\r
+ IotLogging_Puts( pMessageBuffer );\r
+\r
+ /* Free memory used by this function. */\r
+ IotLogging_Free( pMessageBuffer );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS Common V1.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/* FreeRTOS includes. */\r
+#include "FreeRTOS.h"\r
+#include "task.h"\r
+#include "queue.h"\r
+\r
+/* Logging includes. */\r
+#include "iot_logging_task.h"\r
+\r
+/* Standard includes. */\r
+#include <stdio.h>\r
+#include <stdarg.h>\r
+#include <string.h>\r
+\r
+/* Sanity check all the definitions required by this file are set. */\r
+#ifndef configPRINT_STRING\r
+ #error configPRINT_STRING( x ) must be defined in FreeRTOSConfig.h to use this logging file. Set configPRINT_STRING( x ) to a function that outputs a string, where X is the string. For example, #define configPRINT_STRING( x ) MyUARTWriteString( X )\r
+#endif\r
+\r
+#ifndef configLOGGING_MAX_MESSAGE_LENGTH\r
+ #error configLOGGING_MAX_MESSAGE_LENGTH must be defined in FreeRTOSConfig.h to use this logging file. configLOGGING_MAX_MESSAGE_LENGTH sets the size of the buffer into which formatted text is written, so also sets the maximum log message length.\r
+#endif\r
+\r
+#ifndef configLOGGING_INCLUDE_TIME_AND_TASK_NAME\r
+ #error configLOGGING_INCLUDE_TIME_AND_TASK_NAME must be defined in FreeRTOSConfig.h to use this logging file. Set configLOGGING_INCLUDE_TIME_AND_TASK_NAME to 1 to prepend a time stamp, message number and the name of the calling task to each logged message. Otherwise set to 0.\r
+#endif\r
+\r
+/* A block time of 0 just means don't block. */\r
+#define loggingDONT_BLOCK 0\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/*\r
+ * The task that actually performs the print output. Using a separate task\r
+ * enables the use of slow output, such as as a UART, without the task that is\r
+ * outputting the log message having to wait for the message to be completely\r
+ * written. Using a separate task also serializes access to the output port.\r
+ *\r
+ * The structure of this task is very simple; it blocks on a queue to wait for\r
+ * a pointer to a string, sending any received strings to a macro that performs\r
+ * the actual output. The macro is port specific, so implemented outside of\r
+ * this file. This version uses dynamic memory, so the buffer that contained\r
+ * the log message is freed after it has been output.\r
+ */\r
+static void prvLoggingTask( void * pvParameters );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/*\r
+ * The queue used to pass pointers to log messages from the task that created\r
+ * the message to the task that will performs the output.\r
+ */\r
+static QueueHandle_t xQueue = NULL;\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+BaseType_t xLoggingTaskInitialize( uint16_t usStackSize,\r
+ UBaseType_t uxPriority,\r
+ UBaseType_t uxQueueLength )\r
+{\r
+ BaseType_t xReturn = pdFAIL;\r
+\r
+ /* Ensure the logging task has not been created already. */\r
+ if( xQueue == NULL )\r
+ {\r
+ /* Create the queue used to pass pointers to strings to the logging task. */\r
+ xQueue = xQueueCreate( uxQueueLength, sizeof( char ** ) );\r
+\r
+ if( xQueue != NULL )\r
+ {\r
+ if( xTaskCreate( prvLoggingTask, "Logging", usStackSize, NULL, uxPriority, NULL ) == pdPASS )\r
+ {\r
+ xReturn = pdPASS;\r
+ }\r
+ else\r
+ {\r
+ /* Could not create the task, so delete the queue again. */\r
+ vQueueDelete( xQueue );\r
+ }\r
+ }\r
+ }\r
+\r
+ return xReturn;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvLoggingTask( void * pvParameters )\r
+{\r
+ char * pcReceivedString = NULL;\r
+\r
+ for( ; ; )\r
+ {\r
+ /* Block to wait for the next string to print. */\r
+ if( xQueueReceive( xQueue, &pcReceivedString, portMAX_DELAY ) == pdPASS )\r
+ {\r
+ configPRINT_STRING( pcReceivedString );\r
+ vPortFree( ( void * ) pcReceivedString );\r
+ }\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+/*!\r
+ * \brief Formats a string to be printed and sends it\r
+ * to the print queue.\r
+ *\r
+ * Appends the message number, time (in ticks), and task\r
+ * that called vLoggingPrintf to the beginning of each\r
+ * print statement.\r
+ *\r
+ */\r
+void vLoggingPrintf( const char * pcFormat,\r
+ ... )\r
+{\r
+ size_t xLength = 0;\r
+ int32_t xLength2 = 0;\r
+ va_list args;\r
+ char * pcPrintString = NULL;\r
+\r
+ /* The queue is created by xLoggingTaskInitialize(). Check\r
+ * xLoggingTaskInitialize() has been called. */\r
+ configASSERT( xQueue );\r
+\r
+ /* Allocate a buffer to hold the log message. */\r
+ pcPrintString = pvPortMalloc( configLOGGING_MAX_MESSAGE_LENGTH );\r
+\r
+ if( pcPrintString != NULL )\r
+ {\r
+ /* There are a variable number of parameters. */\r
+ va_start( args, pcFormat );\r
+\r
+ if( strcmp( pcFormat, "\n" ) != 0 )\r
+ {\r
+ #if ( configLOGGING_INCLUDE_TIME_AND_TASK_NAME == 1 )\r
+ {\r
+ const char * pcTaskName;\r
+ const char * pcNoTask = "None";\r
+ static BaseType_t xMessageNumber = 0;\r
+\r
+ /* Add a time stamp and the name of the calling task to the\r
+ * start of the log. */\r
+ if( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED )\r
+ {\r
+ pcTaskName = pcTaskGetName( NULL );\r
+ }\r
+ else\r
+ {\r
+ pcTaskName = pcNoTask;\r
+ }\r
+\r
+ xLength = snprintf( pcPrintString, configLOGGING_MAX_MESSAGE_LENGTH, "%lu %lu [%s] ",\r
+ ( unsigned long ) xMessageNumber++,\r
+ ( unsigned long ) xTaskGetTickCount(),\r
+ pcTaskName );\r
+ }\r
+ #else /* if ( configLOGGING_INCLUDE_TIME_AND_TASK_NAME == 1 ) */\r
+ {\r
+ xLength = 0;\r
+ }\r
+ #endif /* if ( configLOGGING_INCLUDE_TIME_AND_TASK_NAME == 1 ) */\r
+ }\r
+\r
+ xLength2 = vsnprintf( pcPrintString + xLength, configLOGGING_MAX_MESSAGE_LENGTH - xLength, pcFormat, args );\r
+\r
+ if( xLength2 < 0 )\r
+ {\r
+ /* vsnprintf() failed. Restore the terminating NULL\r
+ * character of the first part. Note that the first\r
+ * part of the buffer may be empty if the value of\r
+ * configLOGGING_INCLUDE_TIME_AND_TASK_NAME is not\r
+ * 1 and as a result, the whole buffer may be empty.\r
+ * That's the reason we have a check for xLength > 0\r
+ * before sending the buffer to the logging task.\r
+ */\r
+ xLength2 = 0;\r
+ pcPrintString[ xLength ] = '\0';\r
+ }\r
+\r
+ xLength += ( size_t ) xLength2;\r
+ va_end( args );\r
+\r
+ /* Only send the buffer to the logging task if it is\r
+ * not empty. */\r
+ if( xLength > 0 )\r
+ {\r
+ /* Send the string to the logging task for IO. */\r
+ if( xQueueSend( xQueue, &pcPrintString, loggingDONT_BLOCK ) != pdPASS )\r
+ {\r
+ /* The buffer was not sent so must be freed again. */\r
+ vPortFree( ( void * ) pcPrintString );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* The buffer was not sent, so it must be\r
+ * freed. */\r
+ vPortFree( ( void * ) pcPrintString );\r
+ }\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vLoggingPrint( const char * pcMessage )\r
+{\r
+ char * pcPrintString = NULL;\r
+ size_t xLength = 0;\r
+\r
+ /* The queue is created by xLoggingTaskInitialize(). Check\r
+ * xLoggingTaskInitialize() has been called. */\r
+ configASSERT( xQueue );\r
+\r
+ xLength = strlen( pcMessage ) + 1;\r
+ pcPrintString = pvPortMalloc( xLength );\r
+\r
+ if( pcPrintString != NULL )\r
+ {\r
+ strncpy( pcPrintString, pcMessage, xLength );\r
+\r
+ /* Send the string to the logging task for IO. */\r
+ if( xQueueSend( xQueue, &pcPrintString, loggingDONT_BLOCK ) != pdPASS )\r
+ {\r
+ /* The buffer was not sent so must be freed again. */\r
+ vPortFree( ( void * ) pcPrintString );\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt.h\r
+ * @brief User-facing functions of the MQTT 3.1.1 library.\r
+ */\r
+\r
+#ifndef IOT_MQTT_H_\r
+#define IOT_MQTT_H_\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* MQTT types include. */\r
+#include "types/iot_mqtt_types.h"\r
+\r
+/*------------------------- MQTT library functions --------------------------*/\r
+\r
+/**\r
+ * @functionspage{mqtt,MQTT library}\r
+ * - @functionname{mqtt_function_init}\r
+ * - @functionname{mqtt_function_cleanup}\r
+ * - @functionname{mqtt_function_receivecallback}\r
+ * - @functionname{mqtt_function_connect}\r
+ * - @functionname{mqtt_function_disconnect}\r
+ * - @functionname{mqtt_function_subscribe}\r
+ * - @functionname{mqtt_function_timedsubscribe}\r
+ * - @functionname{mqtt_function_unsubscribe}\r
+ * - @functionname{mqtt_function_timedunsubscribe}\r
+ * - @functionname{mqtt_function_publish}\r
+ * - @functionname{mqtt_function_timedpublish}\r
+ * - @functionname{mqtt_function_wait}\r
+ * - @functionname{mqtt_function_strerror}\r
+ * - @functionname{mqtt_function_operationtype}\r
+ * - @functionname{mqtt_function_issubscribed}\r
+ */\r
+\r
+/**\r
+ * @functionpage{IotMqtt_Init,mqtt,init}\r
+ * @functionpage{IotMqtt_Cleanup,mqtt,cleanup}\r
+ * @functionpage{IotMqtt_ReceiveCallback,mqtt,receivecallback}\r
+ * @functionpage{IotMqtt_Connect,mqtt,connect}\r
+ * @functionpage{IotMqtt_Disconnect,mqtt,disconnect}\r
+ * @functionpage{IotMqtt_Subscribe,mqtt,subscribe}\r
+ * @functionpage{IotMqtt_TimedSubscribe,mqtt,timedsubscribe}\r
+ * @functionpage{IotMqtt_Unsubscribe,mqtt,unsubscribe}\r
+ * @functionpage{IotMqtt_TimedUnsubscribe,mqtt,timedunsubscribe}\r
+ * @functionpage{IotMqtt_Publish,mqtt,publish}\r
+ * @functionpage{IotMqtt_TimedPublish,mqtt,timedpublish}\r
+ * @functionpage{IotMqtt_Wait,mqtt,wait}\r
+ * @functionpage{IotMqtt_strerror,mqtt,strerror}\r
+ * @functionpage{IotMqtt_OperationType,mqtt,operationtype}\r
+ * @functionpage{IotMqtt_IsSubscribed,mqtt,issubscribed}\r
+ */\r
+\r
+/**\r
+ * @brief One-time initialization function for the MQTT library.\r
+ *\r
+ * This function performs setup of the MQTT library. <b>It must be called\r
+ * once (and only once) before calling any other MQTT function.</b> Calling this\r
+ * function more than once without first calling @ref mqtt_function_cleanup\r
+ * may result in a crash.\r
+ *\r
+ * @return One of the following:\r
+ * - #IOT_MQTT_SUCCESS\r
+ * - #IOT_MQTT_INIT_FAILED\r
+ *\r
+ * @warning No thread-safety guarantees are provided for this function.\r
+ *\r
+ * @see @ref mqtt_function_cleanup\r
+ */\r
+/* @[declare_mqtt_init] */\r
+IotMqttError_t IotMqtt_Init( void );\r
+/* @[declare_mqtt_init] */\r
+\r
+/**\r
+ * @brief One-time deinitialization function for the MQTT library.\r
+ *\r
+ * This function frees resources taken in @ref mqtt_function_init. It should be\r
+ * called after [closing all MQTT connections](@ref mqtt_function_disconnect) to\r
+ * clean up the MQTT library. After this function returns, @ref mqtt_function_init\r
+ * must be called again before calling any other MQTT function.\r
+ *\r
+ * @warning No thread-safety guarantees are provided for this function. Do not\r
+ * call this function if any MQTT connections are open!\r
+ *\r
+ * @see @ref mqtt_function_init\r
+ */\r
+/* @[declare_mqtt_cleanup] */\r
+void IotMqtt_Cleanup( void );\r
+/* @[declare_mqtt_cleanup] */\r
+\r
+/**\r
+ * @brief Network receive callback for the MQTT library.\r
+ *\r
+ * This function should be called by the system whenever data is available for\r
+ * the MQTT library.\r
+ *\r
+ * @param[in] pNetworkConnection The network connection associated with the MQTT\r
+ * connection, passed by the network stack.\r
+ * @param[in] pReceiveContext A pointer to the MQTT connection handle for which\r
+ * the packet was received.\r
+ */\r
+/* @[declare_mqtt_receivecallback] */\r
+void IotMqtt_ReceiveCallback( void * pNetworkConnection,\r
+ void * pReceiveContext );\r
+/* @[declare_mqtt_receivecallback] */\r
+\r
+/**\r
+ * @brief Establish a new MQTT connection.\r
+ *\r
+ * This function opens a connection between a new MQTT client and an MQTT server\r
+ * (also called a <i>broker</i>). MQTT connections are established on top of transport\r
+ * layer protocols (such as TCP/IP), and optionally, application layer security\r
+ * protocols (such as TLS). The MQTT packet that establishes a connection is called\r
+ * the MQTT CONNECT packet. After @ref mqtt_function_init, this function must be\r
+ * called before any other MQTT library function.\r
+ *\r
+ * If [pConnectInfo->cleanSession](@ref IotMqttConnectInfo_t.cleanSession) is `true`,\r
+ * this function establishes a clean MQTT session. Subscriptions and unacknowledged\r
+ * PUBLISH messages will be discarded when the connection is closed.\r
+ *\r
+ * If [pConnectInfo->cleanSession](@ref IotMqttConnectInfo_t.cleanSession) is `false`,\r
+ * this function establishes (or re-establishes) a persistent MQTT session. The parameters\r
+ * [pConnectInfo->pPreviousSubscriptions](@ref IotMqttConnectInfo_t.pPreviousSubscriptions)\r
+ * and [pConnectInfo->previousSubscriptionCount](@ref IotMqttConnectInfo_t.previousSubscriptionCount)\r
+ * may be used to restore subscriptions present in a re-established persistent session.\r
+ * Any restored subscriptions <b>MUST</b> have been present in the persistent session;\r
+ * <b>this function does not send an MQTT SUBSCRIBE packet!</b>\r
+ *\r
+ * This MQTT library is network agnostic, meaning it has no knowledge of the\r
+ * underlying network protocol carrying the MQTT packets. It interacts with the\r
+ * network through a network abstraction layer, allowing it to be used with many\r
+ * different network stacks. The network abstraction layer is established\r
+ * per-connection, allowing every #IotMqttConnection_t to use a different network\r
+ * stack. The parameter `pNetworkInterface` sets up the network abstraction layer\r
+ * for an MQTT connection; see the documentation on #IotMqttNetworkInfo_t for details\r
+ * on its members.\r
+ *\r
+ * The `pConnectInfo` parameter provides the contents of the MQTT CONNECT packet.\r
+ * Most members [are defined by the MQTT spec.](@ref IotMqttConnectInfo_t). The\r
+ * [pConnectInfo->pWillInfo](@ref IotMqttConnectInfo_t.pWillInfo) member provides\r
+ * information on a Last Will and Testament (LWT) message to be published if the\r
+ * MQTT connection is closed without [sending a DISCONNECT packet]\r
+ * (@ref mqtt_function_disconnect). Unlike other PUBLISH\r
+ * messages, a LWT message payload is limited to 65535 bytes in length. Additionally,\r
+ * the retry [interval](@ref IotMqttPublishInfo_t.retryMs) and [limit]\r
+ * (@ref IotMqttPublishInfo_t.retryLimit) members of #IotMqttPublishInfo_t\r
+ * are ignored for LWT messages. The LWT message is optional; `pWillInfo` may be NULL.\r
+ *\r
+ * Unlike @ref mqtt_function_publish, @ref mqtt_function_subscribe, and\r
+ * @ref mqtt_function_unsubscribe, this function is always blocking. Additionally,\r
+ * because the MQTT connection acknowledgement packet (CONNACK packet) does not\r
+ * contain any information on <i>which</i> CONNECT packet it acknowledges, only one\r
+ * CONNECT operation may be in progress at any time. This means that parallel\r
+ * threads making calls to @ref mqtt_function_connect will be serialized to send\r
+ * their CONNECT packets one-by-one.\r
+ *\r
+ * @param[in] pNetworkInfo Information on the transport-layer network connection\r
+ * to use with the MQTT connection.\r
+ * @param[in] pConnectInfo MQTT connection setup parameters.\r
+ * @param[in] timeoutMs If the MQTT server does not accept the connection within\r
+ * this timeout, this function returns #IOT_MQTT_TIMEOUT.\r
+ * @param[out] pMqttConnection Set to a newly-initialized MQTT connection handle\r
+ * if this function succeeds.\r
+ *\r
+ * @return One of the following:\r
+ * - #IOT_MQTT_SUCCESS\r
+ * - #IOT_MQTT_BAD_PARAMETER\r
+ * - #IOT_MQTT_NO_MEMORY\r
+ * - #IOT_MQTT_NETWORK_ERROR\r
+ * - #IOT_MQTT_SCHEDULING_ERROR\r
+ * - #IOT_MQTT_BAD_RESPONSE\r
+ * - #IOT_MQTT_TIMEOUT\r
+ * - #IOT_MQTT_SERVER_REFUSED\r
+ *\r
+ * <b>Example</b>\r
+ * @code{c}\r
+ * // An initialized and connected network connection.\r
+ * IotNetworkConnection_t pNetworkConnection;\r
+ *\r
+ * // Parameters to MQTT connect.\r
+ * IotMqttConnection_t mqttConnection = IOT_MQTT_CONNECTION_INITIALIZER;\r
+ * IotMqttNetworkInfo_t networkInfo = IOT_MQTT_NETWORK_INFO_INITIALIZER;\r
+ * IotMqttConnectInfo_t connectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER;\r
+ * IotMqttPublishInfo_t willInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;\r
+ *\r
+ * // Example network abstraction types.\r
+ * IotNetworkServerInfo_t serverInfo = { ... };\r
+ * IotNetworkCredentialInfo_t credentialInfo = { ... };\r
+ * IotNetworkInterface_t networkInterface = { ... };\r
+ *\r
+ * // Example using a generic network implementation.\r
+ * networkInfo.createNetworkConnection = true;\r
+ * networkInfo.pNetworkServerInfo = &serverInfo;\r
+ * networkInfo.pNetworkCredentialInfo = &credentialInfo;\r
+ * networkInfo.pNetworkInterface = &networkInterface;\r
+ *\r
+ * // Set the members of the connection info (password and username not used).\r
+ * connectInfo.cleanSession = true;\r
+ * connectInfo.keepAliveSeconds = 30;\r
+ * connectInfo.pClientIdentifier = "uniqueclientidentifier";\r
+ * connectInfo.clientIdentifierLength = 22;\r
+ *\r
+ * // Set the members of the will info (retain and retry not used).\r
+ * willInfo.qos = IOT_MQTT_QOS_1;\r
+ * willInfo.pTopicName = "will/topic/name";\r
+ * willInfo.topicNameLength = 15;\r
+ * willInfo.pPayload = "MQTT client unexpectedly disconnected.";\r
+ * willInfo.payloadLength = 38;\r
+ *\r
+ * // Set the pointer to the will info.\r
+ * connectInfo.pWillInfo = &willInfo;\r
+ *\r
+ * // Call CONNECT with a 5 second block time. Should return\r
+ * // IOT_MQTT_SUCCESS when successful.\r
+ * IotMqttError_t result = IotMqtt_Connect( &networkInfo,\r
+ * &connectInfo,\r
+ * 5000,\r
+ * &mqttConnection );\r
+ *\r
+ * if( result == IOT_MQTT_SUCCESS )\r
+ * {\r
+ * // Do something with the MQTT connection...\r
+ *\r
+ * // Clean up and close the MQTT connection once it's no longer needed.\r
+ * IotMqtt_Disconnect( mqttConnection, 0 );\r
+ * }\r
+ * @endcode\r
+ */\r
+/* @[declare_mqtt_connect] */\r
+IotMqttError_t IotMqtt_Connect( const IotMqttNetworkInfo_t * pNetworkInfo,\r
+ const IotMqttConnectInfo_t * pConnectInfo,\r
+ uint32_t timeoutMs,\r
+ IotMqttConnection_t * const pMqttConnection );\r
+/* @[declare_mqtt_connect] */\r
+\r
+/**\r
+ * @brief Closes an MQTT connection and frees resources.\r
+ *\r
+ * This function closes an MQTT connection and should only be called once\r
+ * the MQTT connection is no longer needed. Its exact behavior depends on the\r
+ * `flags` parameter.\r
+ *\r
+ * Normally, `flags` should be `0`. This gracefully shuts down an MQTT\r
+ * connection by sending an MQTT DISCONNECT packet. Any [network close function]\r
+ * (@ref IotNetworkInterface_t::close) provided [when the connection was established]\r
+ * (@ref mqtt_function_connect) will also be called. Note that because the MQTT server\r
+ * will not acknowledge a DISCONNECT packet, the client has no way of knowing if\r
+ * the server received the DISCONNECT packet. In the case where the DISCONNECT\r
+ * packet is lost in transport, any Last Will and Testament (LWT) message established\r
+ * with the connection may be published. However, if the DISCONNECT reaches the\r
+ * MQTT server, the LWT message will be discarded and not published.\r
+ *\r
+ * Should the underlying network connection become unusable, this function should\r
+ * be called with `flags` set to #IOT_MQTT_FLAG_CLEANUP_ONLY. In this case, no\r
+ * DISCONNECT packet will be sent, though the [network close function](@ref IotNetworkInterface_t::close)\r
+ * will still be called. This function will only free the resources used by the MQTT\r
+ * connection; it still must be called even if the network is offline to avoid leaking\r
+ * resources.\r
+ *\r
+ * Once this function is called, its parameter `mqttConnection` should no longer\r
+ * be used.\r
+ *\r
+ * @param[in] mqttConnection The MQTT connection to close and clean up.\r
+ * @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.\r
+ */\r
+/* @[declare_mqtt_disconnect] */\r
+void IotMqtt_Disconnect( IotMqttConnection_t mqttConnection,\r
+ uint32_t flags );\r
+/* @[declare_mqtt_disconnect] */\r
+\r
+/**\r
+ * @brief Subscribes to the given array of topic filters and receive an asynchronous\r
+ * notification when the subscribe completes.\r
+ *\r
+ * This function transmits an MQTT SUBSCRIBE packet to the server. A SUBSCRIBE\r
+ * packet notifies the server to send any matching PUBLISH messages to this client.\r
+ * A single SUBSCRIBE packet may carry more than one topic filter, hence the\r
+ * parameters to this function include an array of [subscriptions]\r
+ * (@ref IotMqttSubscription_t).\r
+ *\r
+ * An MQTT subscription has two pieces:\r
+ * 1. The subscription topic filter registered with the MQTT server. The MQTT\r
+ * SUBSCRIBE packet sent from this client to server notifies the server to send\r
+ * messages matching the given topic filters to this client.\r
+ * 2. The [callback function](@ref IotMqttCallbackInfo_t.function) that this\r
+ * client will invoke when an incoming message is received. The callback function\r
+ * notifies applications of an incoming PUBLISH message.\r
+ *\r
+ * The helper function @ref mqtt_function_issubscribed can be used to check if a\r
+ * [callback function](@ref IotMqttCallbackInfo_t.function) is registered for\r
+ * a particular topic filter.\r
+ *\r
+ * To modify an already-registered subscription callback, call this function with\r
+ * a new `pSubscriptionList`. Any topic filters in `pSubscriptionList` that already\r
+ * have a registered callback will be replaced with the new values in `pSubscriptionList`.\r
+ *\r
+ * @attention QoS 2 subscriptions are currently unsupported. Only 0 or 1 are valid\r
+ * for subscription QoS.\r
+ *\r
+ * @param[in] mqttConnection The MQTT connection to use for the subscription.\r
+ * @param[in] pSubscriptionList Pointer to the first element in the array of\r
+ * subscriptions.\r
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.\r
+ * @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.\r
+ * @param[in] pCallbackInfo Asynchronous notification of this function's completion.\r
+ * @param[out] pSubscribeOperation Set to a handle by which this operation may be\r
+ * referenced after this function returns. This reference is invalidated once\r
+ * the subscription operation completes.\r
+ *\r
+ * @return This function will return #IOT_MQTT_STATUS_PENDING upon success.\r
+ * @return Upon completion of the subscription (either through an\r
+ * #IotMqttCallbackInfo_t or @ref mqtt_function_wait), the status will be one of:\r
+ * - #IOT_MQTT_SUCCESS\r
+ * - #IOT_MQTT_NETWORK_ERROR\r
+ * - #IOT_MQTT_SCHEDULING_ERROR\r
+ * - #IOT_MQTT_BAD_RESPONSE\r
+ * - #IOT_MQTT_SERVER_REFUSED\r
+ * @return If this function fails before queuing a subscribe operation, it will return\r
+ * one of:\r
+ * - #IOT_MQTT_BAD_PARAMETER\r
+ * - #IOT_MQTT_NO_MEMORY\r
+ *\r
+ * @see @ref mqtt_function_timedsubscribe for a blocking variant of this function.\r
+ * @see @ref mqtt_function_unsubscribe for the function that removes subscriptions.\r
+ *\r
+ * <b>Example</b>\r
+ * @code{c}\r
+ * #define NUMBER_OF_SUBSCRIPTIONS ...\r
+ *\r
+ * // Subscription callback function.\r
+ * void subscriptionCallback( void * pArgument, IotMqttCallbackParam_t * pPublish );\r
+ *\r
+ * // An initialized and connected MQTT connection.\r
+ * IotMqttConnection_t mqttConnection;\r
+ *\r
+ * // Subscription information.\r
+ * pSubscriptions[ NUMBER_OF_SUBSCRIPTIONS ] = { IOT_MQTT_SUBSCRIPTION_INITIALIZER };\r
+ * IotMqttOperation_t lastOperation = IOT_MQTT_OPERATION_INITIALIZER;\r
+ *\r
+ * // Set the subscription information.\r
+ * for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )\r
+ * {\r
+ * pSubscriptions[ i ].qos = IOT_MQTT_QOS_1;\r
+ * pSubscriptions[ i ].pTopicFilter = "some/topic/filter";\r
+ * pSubscriptions[ i ].topicLength = ( uint16_t ) strlen( pSubscriptions[ i ].pTopicFilter );\r
+ * pSubscriptions[ i ].callback.function = subscriptionCallback;\r
+ * }\r
+ *\r
+ * IotMqttError_t result = IotMqtt_Subscribe( mqttConnection,\r
+ * pSubscriptions,\r
+ * NUMBER_OF_SUBSCRIPTIONS,\r
+ * IOT_MQTT_FLAG_WAITABLE,\r
+ * NULL,\r
+ * &lastOperation );\r
+ *\r
+ * // Subscribe returns IOT_MQTT_STATUS_PENDING when successful. Wait up to\r
+ * // 5 seconds for the operation to complete.\r
+ * if( result == IOT_MQTT_STATUS_PENDING )\r
+ * {\r
+ * result = IotMqtt_Wait( subscriptionRef, 5000 );\r
+ * }\r
+ *\r
+ * // Check that the subscriptions were successful.\r
+ * if( result == IOT_MQTT_SUCCESS )\r
+ * {\r
+ * // Wait for messages on the subscription topic filters...\r
+ *\r
+ * // Unsubscribe once the subscriptions are no longer needed.\r
+ * result = IotMqtt_Unsubscribe( mqttConnection,\r
+ * pSubscriptions,\r
+ * NUMBER_OF_SUBSCRIPTIONS,\r
+ * IOT_MQTT_FLAG_WAITABLE,\r
+ * NULL,\r
+ * &lastOperation );\r
+ *\r
+ * // UNSUBSCRIBE returns IOT_MQTT_STATUS_PENDING when successful.\r
+ * // Wait up to 5 seconds for the operation to complete.\r
+ * if( result == IOT_MQTT_STATUS_PENDING )\r
+ * {\r
+ * result = IotMqtt_Wait( lastOperation, 5000 );\r
+ * }\r
+ * }\r
+ * // Check which subscriptions were rejected by the server.\r
+ * else if( result == IOT_MQTT_SERVER_REFUSED )\r
+ * {\r
+ * for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )\r
+ * {\r
+ * if( IotMqtt_IsSubscribed( mqttConnection,\r
+ * pSubscriptions[ i ].pTopicFilter,\r
+ * pSubscriptions[ i ].topicFilterLength,\r
+ * NULL ) == false )\r
+ * {\r
+ * // This subscription was rejected.\r
+ * }\r
+ * }\r
+ * }\r
+ * @endcode\r
+ */\r
+/* @[declare_mqtt_subscribe] */\r
+IotMqttError_t IotMqtt_Subscribe( IotMqttConnection_t mqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint32_t flags,\r
+ const IotMqttCallbackInfo_t * pCallbackInfo,\r
+ IotMqttOperation_t * pSubscribeOperation );\r
+/* @[declare_mqtt_subscribe] */\r
+\r
+/**\r
+ * @brief Subscribes to the given array of topic filters with a timeout.\r
+ *\r
+ * This function transmits an MQTT SUBSCRIBE packet to the server, then waits for\r
+ * a server response to the packet. Internally, this function is a call to @ref\r
+ * mqtt_function_subscribe followed by @ref mqtt_function_wait. See @ref\r
+ * mqtt_function_subscribe for more information about the MQTT SUBSCRIBE operation.\r
+ *\r
+ * @attention QoS 2 subscriptions are currently unsupported. Only 0 or 1 are valid\r
+ * for subscription QoS.\r
+ *\r
+ * @param[in] mqttConnection The MQTT connection to use for the subscription.\r
+ * @param[in] pSubscriptionList Pointer to the first element in the array of\r
+ * subscriptions.\r
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.\r
+ * @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.\r
+ * Currently, flags are ignored by this function; this parameter is for\r
+ * future-compatibility.\r
+ * @param[in] timeoutMs If the MQTT server does not acknowledge the subscriptions within\r
+ * this timeout, this function returns #IOT_MQTT_TIMEOUT.\r
+ *\r
+ * @return One of the following:\r
+ * - #IOT_MQTT_SUCCESS\r
+ * - #IOT_MQTT_BAD_PARAMETER\r
+ * - #IOT_MQTT_NO_MEMORY\r
+ * - #IOT_MQTT_NETWORK_ERROR\r
+ * - #IOT_MQTT_SCHEDULING_ERROR\r
+ * - #IOT_MQTT_BAD_RESPONSE\r
+ * - #IOT_MQTT_TIMEOUT\r
+ * - #IOT_MQTT_SERVER_REFUSED\r
+ */\r
+/* @[declare_mqtt_timedsubscribe] */\r
+IotMqttError_t IotMqtt_TimedSubscribe( IotMqttConnection_t mqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint32_t flags,\r
+ uint32_t timeoutMs );\r
+/* @[declare_mqtt_timedsubscribe] */\r
+\r
+/**\r
+ * @brief Unsubscribes from the given array of topic filters and receive an asynchronous\r
+ * notification when the unsubscribe completes.\r
+ *\r
+ * This function transmits an MQTT UNSUBSCRIBE packet to the server. An UNSUBSCRIBE\r
+ * packet removes registered topic filters from the server. After unsubscribing,\r
+ * the server will no longer send messages on these topic filters to the client.\r
+ *\r
+ * Corresponding [subscription callback functions](@ref IotMqttCallbackInfo_t.function)\r
+ * are also removed from the MQTT connection. These subscription callback functions\r
+ * will be removed even if the MQTT UNSUBSCRIBE packet fails to send.\r
+ *\r
+ * @param[in] mqttConnection The MQTT connection used for the subscription.\r
+ * @param[in] pSubscriptionList Pointer to the first element in the array of\r
+ * subscriptions.\r
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.\r
+ * @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.\r
+ * @param[in] pCallbackInfo Asynchronous notification of this function's completion.\r
+ * @param[out] pUnsubscribeOperation Set to a handle by which this operation may be\r
+ * referenced after this function returns. This reference is invalidated once\r
+ * the unsubscribe operation completes.\r
+ *\r
+ * @return This function will return #IOT_MQTT_STATUS_PENDING upon success.\r
+ * @return Upon completion of the unsubscribe (either through an\r
+ * #IotMqttCallbackInfo_t or @ref mqtt_function_wait), the status will be one of:\r
+ * - #IOT_MQTT_SUCCESS\r
+ * - #IOT_MQTT_NETWORK_ERROR\r
+ * - #IOT_MQTT_SCHEDULING_ERROR\r
+ * - #IOT_MQTT_BAD_RESPONSE\r
+ * @return If this function fails before queuing an unsubscribe operation, it will return\r
+ * one of:\r
+ * - #IOT_MQTT_BAD_PARAMETER\r
+ * - #IOT_MQTT_NO_MEMORY\r
+ *\r
+ * @see @ref mqtt_function_timedsubscribe for a blocking variant of this function.\r
+ * @see @ref mqtt_function_subscribe for the function that adds subscriptions.\r
+ */\r
+/* @[declare_mqtt_unsubscribe] */\r
+IotMqttError_t IotMqtt_Unsubscribe( IotMqttConnection_t mqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint32_t flags,\r
+ const IotMqttCallbackInfo_t * pCallbackInfo,\r
+ IotMqttOperation_t * pUnsubscribeOperation );\r
+/* @[declare_mqtt_unsubscribe] */\r
+\r
+/**\r
+ * @brief Unsubscribes from a given array of topic filters with a timeout.\r
+ *\r
+ * This function transmits an MQTT UNSUBSCRIBE packet to the server, then waits\r
+ * for a server response to the packet. Internally, this function is a call to\r
+ * @ref mqtt_function_unsubscribe followed by @ref mqtt_function_wait. See @ref\r
+ * mqtt_function_unsubscribe for more information about the MQTT UNSUBSCRIBE\r
+ * operation.\r
+ *\r
+ * @param[in] mqttConnection The MQTT connection used for the subscription.\r
+ * @param[in] pSubscriptionList Pointer to the first element in the array of\r
+ * subscriptions.\r
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.\r
+ * @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.\r
+ * Currently, flags are ignored by this function; this parameter is for\r
+ * future-compatibility.\r
+ * @param[in] timeoutMs If the MQTT server does not acknowledge the UNSUBSCRIBE within\r
+ * this timeout, this function returns #IOT_MQTT_TIMEOUT.\r
+ *\r
+ * @return One of the following:\r
+ * - #IOT_MQTT_SUCCESS\r
+ * - #IOT_MQTT_BAD_PARAMETER\r
+ * - #IOT_MQTT_NO_MEMORY\r
+ * - #IOT_MQTT_NETWORK_ERROR\r
+ * - #IOT_MQTT_SCHEDULING_ERROR\r
+ * - #IOT_MQTT_BAD_RESPONSE\r
+ */\r
+/* @[declare_mqtt_timedunsubscribe] */\r
+IotMqttError_t IotMqtt_TimedUnsubscribe( IotMqttConnection_t mqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint32_t flags,\r
+ uint32_t timeoutMs );\r
+/* @[declare_mqtt_timedunsubscribe] */\r
+\r
+/**\r
+ * @brief Publishes a message to the given topic name and receive an asynchronous\r
+ * notification when the publish completes.\r
+ *\r
+ * This function transmits an MQTT PUBLISH packet to the server. A PUBLISH packet\r
+ * contains a payload and a topic name. Any clients with a subscription on a\r
+ * topic filter matching the PUBLISH topic name will receive a copy of the\r
+ * PUBLISH packet from the server.\r
+ *\r
+ * If a PUBLISH packet fails to reach the server and it is not a QoS 0 message,\r
+ * it will be retransmitted. See #IotMqttPublishInfo_t for a description\r
+ * of the retransmission strategy.\r
+ *\r
+ * @attention QoS 2 messages are currently unsupported. Only 0 or 1 are valid\r
+ * for message QoS.\r
+ *\r
+ * @param[in] mqttConnection The MQTT connection to use for the publish.\r
+ * @param[in] pPublishInfo MQTT publish parameters.\r
+ * @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.\r
+ * @param[in] pCallbackInfo Asynchronous notification of this function's completion.\r
+ * @param[out] pPublishOperation Set to a handle by which this operation may be\r
+ * referenced after this function returns. This reference is invalidated once\r
+ * the publish operation completes.\r
+ *\r
+ * @return This function will return #IOT_MQTT_STATUS_PENDING upon success for\r
+ * QoS 1 publishes. For a QoS 0 publish it returns #IOT_MQTT_SUCCESS upon\r
+ * success.\r
+ * @return Upon completion of a QoS 1 publish (either through an\r
+ * #IotMqttCallbackInfo_t or @ref mqtt_function_wait), the status will be one of:\r
+ * - #IOT_MQTT_SUCCESS\r
+ * - #IOT_MQTT_NETWORK_ERROR\r
+ * - #IOT_MQTT_SCHEDULING_ERROR\r
+ * - #IOT_MQTT_BAD_RESPONSE\r
+ * - #IOT_MQTT_RETRY_NO_RESPONSE (if [pPublishInfo->retryMs](@ref IotMqttPublishInfo_t.retryMs)\r
+ * and [pPublishInfo->retryLimit](@ref IotMqttPublishInfo_t.retryLimit) were set).\r
+ * @return If this function fails before queuing an publish operation (regardless\r
+ * of QoS), it will return one of:\r
+ * - #IOT_MQTT_BAD_PARAMETER\r
+ * - #IOT_MQTT_NO_MEMORY\r
+ *\r
+ * @note The parameters `pCallbackInfo` and `pPublishOperation` should only be used for QoS\r
+ * 1 publishes. For QoS 0, they should both be `NULL`.\r
+ *\r
+ * @see @ref mqtt_function_timedpublish for a blocking variant of this function.\r
+ *\r
+ * <b>Example</b>\r
+ * @code{c}\r
+ * // An initialized and connected MQTT connection.\r
+ * IotMqttConnection_t mqttConnection;\r
+ *\r
+ * // Publish information.\r
+ * IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;\r
+ *\r
+ * // Set the publish information. QoS 0 example (retain not used):\r
+ * publishInfo.qos = IOT_MQTT_QOS_0;\r
+ * publishInfo.pTopicName = "some/topic/name";\r
+ * publishInfo.topicNameLength = 15;\r
+ * publishInfo.pPayload = "payload";\r
+ * publishInfo.payloadLength = 8;\r
+ *\r
+ * // QoS 0 publish should return IOT_MQTT_SUCCESS upon success.\r
+ * IotMqttError_t qos0Result = IotMqtt_Publish( mqttConnection,\r
+ * &publishInfo,\r
+ * 0,\r
+ * NULL,\r
+ * NULL );\r
+ *\r
+ * // QoS 1 with retry example (using same topic name and payload as QoS 0 example):\r
+ * IotMqttOperation_t qos1Operation = IOT_MQTT_OPERATION_INITIALIZER;\r
+ * publishInfo.qos = IOT_MQTT_QOS_1;\r
+ * publishInfo.retryMs = 1000; // Retry if no response is received in 1 second.\r
+ * publishInfo.retryLimit = 5; // Retry up to 5 times.\r
+ *\r
+ * // QoS 1 publish should return IOT_MQTT_STATUS_PENDING upon success.\r
+ * IotMqttError_t qos1Result = IotMqtt_Publish( mqttConnection,\r
+ * &publishInfo,\r
+ * IOT_MQTT_FLAG_WAITABLE,\r
+ * NULL,\r
+ * &qos1Operation );\r
+ *\r
+ * // Wait up to 5 seconds for the publish to complete.\r
+ * if( qos1Result == IOT_MQTT_STATUS_PENDING )\r
+ * {\r
+ * qos1Result = IotMqtt_Wait( qos1Operation, 5000 );\r
+ * }\r
+ * @endcode\r
+ */\r
+/* @[declare_mqtt_publish] */\r
+IotMqttError_t IotMqtt_Publish( IotMqttConnection_t mqttConnection,\r
+ const IotMqttPublishInfo_t * pPublishInfo,\r
+ uint32_t flags,\r
+ const IotMqttCallbackInfo_t * pCallbackInfo,\r
+ IotMqttOperation_t * pPublishOperation );\r
+/* @[declare_mqtt_publish] */\r
+\r
+/**\r
+ * @brief Publish a message to the given topic name with a timeout.\r
+ *\r
+ * This function transmits an MQTT PUBLISH packet to the server, then waits for\r
+ * a server response to the packet. Internally, this function is a call to @ref\r
+ * mqtt_function_publish followed by @ref mqtt_function_wait. See @ref\r
+ * mqtt_function_publish for more information about the MQTT PUBLISH operation.\r
+ *\r
+ * @attention QoS 2 messages are currently unsupported. Only 0 or 1 are valid\r
+ * for message QoS.\r
+ *\r
+ * @param[in] mqttConnection The MQTT connection to use for the publish.\r
+ * @param[in] pPublishInfo MQTT publish parameters.\r
+ * @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.\r
+ * Currently, flags are ignored by this function; this parameter is for\r
+ * future-compatibility.\r
+ * @param[in] timeoutMs If the MQTT server does not acknowledge a QoS 1 PUBLISH\r
+ * within this timeout, this function returns #IOT_MQTT_TIMEOUT. This parameter\r
+ * is ignored for QoS 0 PUBLISH messages.\r
+ *\r
+ * @return One of the following:\r
+ * - #IOT_MQTT_SUCCESS\r
+ * - #IOT_MQTT_BAD_PARAMETER\r
+ * - #IOT_MQTT_NO_MEMORY\r
+ * - #IOT_MQTT_NETWORK_ERROR\r
+ * - #IOT_MQTT_SCHEDULING_ERROR\r
+ * - #IOT_MQTT_BAD_RESPONSE\r
+ * - #IOT_MQTT_RETRY_NO_RESPONSE (if [pPublishInfo->retryMs](@ref IotMqttPublishInfo_t.retryMs)\r
+ * and [pPublishInfo->retryLimit](@ref IotMqttPublishInfo_t.retryLimit) were set).\r
+ */\r
+/* @[declare_mqtt_timedpublish] */\r
+IotMqttError_t IotMqtt_TimedPublish( IotMqttConnection_t mqttConnection,\r
+ const IotMqttPublishInfo_t * pPublishInfo,\r
+ uint32_t flags,\r
+ uint32_t timeoutMs );\r
+/* @[declare_mqtt_timedpublish] */\r
+\r
+/**\r
+ * @brief Waits for an operation to complete.\r
+ *\r
+ * This function blocks to wait for a [subscribe](@ref mqtt_function_subscribe),\r
+ * [unsubscribe](@ref mqtt_function_unsubscribe), or [publish]\r
+ * (@ref mqtt_function_publish) to complete. These operations are by default\r
+ * asynchronous; the function calls queue an operation for processing, and a\r
+ * callback is invoked once the operation is complete.\r
+ *\r
+ * To use this function, the flag #IOT_MQTT_FLAG_WAITABLE must have been\r
+ * set in the operation's function call. Additionally, this function must always\r
+ * be called with any waitable operation to clean up resources.\r
+ *\r
+ * Regardless of its return value, this function always clean up resources used\r
+ * by the waitable operation. This means `reference` is invalidated as soon as\r
+ * this function returns, even if it returns #IOT_MQTT_TIMEOUT or another error.\r
+ *\r
+ * @param[in] operation Reference to the operation to wait for. The flag\r
+ * #IOT_MQTT_FLAG_WAITABLE must have been set for this operation.\r
+ * @param[in] timeoutMs How long to wait before returning #IOT_MQTT_TIMEOUT.\r
+ *\r
+ * @return The return value of this function depends on the MQTT operation associated\r
+ * with `reference`. See #IotMqttError_t for possible return values.\r
+ *\r
+ * <b>Example</b>\r
+ * @code{c}\r
+ * // Operation reference and timeout.\r
+ * IotMqttOperation_t publishOperation = IOT_MQTT_OPERATION_INITIALIZER;\r
+ * uint32_t timeoutMs = 5000; // 5 seconds\r
+ *\r
+ * // MQTT operation to wait for.\r
+ * IotMqttError_t result = IotMqtt_Publish( mqttConnection,\r
+ * &publishInfo,\r
+ * IOT_MQTT_FLAG_WAITABLE,\r
+ * NULL,\r
+ * &publishOperation );\r
+ *\r
+ * // Publish should have returned IOT_MQTT_STATUS_PENDING. The call to wait\r
+ * // returns once the result of the publish is available or the timeout expires.\r
+ * if( result == IOT_MQTT_STATUS_PENDING )\r
+ * {\r
+ * result = IotMqtt_Wait( publishOperation, timeoutMs );\r
+ *\r
+ * // After the call to wait, the result of the publish is known\r
+ * // (not IOT_MQTT_STATUS_PENDING).\r
+ * assert( result != IOT_MQTT_STATUS_PENDING );\r
+ * }\r
+ * @endcode\r
+ */\r
+/* @[declare_mqtt_wait] */\r
+IotMqttError_t IotMqtt_Wait( IotMqttOperation_t operation,\r
+ uint32_t timeoutMs );\r
+/* @[declare_mqtt_wait] */\r
+\r
+/*-------------------------- MQTT helper functions --------------------------*/\r
+\r
+/**\r
+ * @brief Returns a string that describes an #IotMqttError_t.\r
+ *\r
+ * Like the POSIX's `strerror`, this function returns a string describing a\r
+ * return code. In this case, the return code is an MQTT library error code,\r
+ * `status`.\r
+ *\r
+ * The string returned by this function <b>MUST</b> be treated as read-only: any\r
+ * attempt to modify its contents may result in a crash. Therefore, this function\r
+ * is limited to usage in logging.\r
+ *\r
+ * @param[in] status The status to describe.\r
+ *\r
+ * @return A read-only string that describes `status`.\r
+ *\r
+ * @warning The string returned by this function must never be modified.\r
+ */\r
+/* @[declare_mqtt_strerror] */\r
+const char * IotMqtt_strerror( IotMqttError_t status );\r
+/* @[declare_mqtt_strerror] */\r
+\r
+/**\r
+ * @brief Returns a string that describes an #IotMqttOperationType_t.\r
+ *\r
+ * This function returns a string describing an MQTT operation type, `operation`.\r
+ *\r
+ * The string returned by this function <b>MUST</b> be treated as read-only: any\r
+ * attempt to modify its contents may result in a crash. Therefore, this function\r
+ * is limited to usage in logging.\r
+ *\r
+ * @param[in] operation The operation to describe.\r
+ *\r
+ * @return A read-only string that describes `operation`.\r
+ *\r
+ * @warning The string returned by this function must never be modified.\r
+ */\r
+/* @[declare_mqtt_operationtype] */\r
+const char * IotMqtt_OperationType( IotMqttOperationType_t operation );\r
+/* @[declare_mqtt_operationtype] */\r
+\r
+/**\r
+ * @brief Check if an MQTT connection has a subscription for a topic filter.\r
+ *\r
+ * This function checks whether an MQTT connection `mqttConnection` has a\r
+ * subscription callback registered for a topic filter `pTopicFilter`. If a\r
+ * subscription callback is found, its details are copied into the output parameter\r
+ * `pCurrentSubscription`. This subscription callback will be invoked for incoming\r
+ * PUBLISH messages on `pTopicFilter`.\r
+ *\r
+ * <b>The check for a matching subscription is only performed client-side</b>;\r
+ * therefore, this function should not be relied upon for perfect accuracy. For\r
+ * example, this function may return an incorrect result if the MQTT server\r
+ * crashes and drops subscriptions without informing the client.\r
+ *\r
+ * Note that an MQTT connection's subscriptions might change between the time this\r
+ * function checks the subscription list and its caller tests the return value.\r
+ * This function certainly should not be used concurrently with any pending SUBSCRIBE\r
+ * or UNSUBSCRIBE operations.\r
+ *\r
+ * One suitable use of this function is to check <i>which</i> subscriptions were rejected\r
+ * if @ref mqtt_function_subscribe returns #IOT_MQTT_SERVER_REFUSED; that return\r
+ * code only means that <i>at least one</i> subscription was rejected.\r
+ *\r
+ * @param[in] mqttConnection The MQTT connection to check.\r
+ * @param[in] pTopicFilter The topic filter to check.\r
+ * @param[in] topicFilterLength Length of `pTopicFilter`.\r
+ * @param[out] pCurrentSubscription If a subscription is found, its details are\r
+ * copied here. This output parameter is only valid if this function returns `true`.\r
+ * Pass `NULL` to ignore.\r
+ *\r
+ * @return `true` if a subscription was found; `false` otherwise.\r
+ *\r
+ * @note The subscription QoS is not stored by the MQTT library; therefore,\r
+ * `pCurrentSubscription->qos` will always be set to #IOT_MQTT_QOS_0.\r
+ */\r
+/* @[declare_mqtt_issubscribed] */\r
+bool IotMqtt_IsSubscribed( IotMqttConnection_t mqttConnection,\r
+ const char * pTopicFilter,\r
+ uint16_t topicFilterLength,\r
+ IotMqttSubscription_t * pCurrentSubscription );\r
+/* @[declare_mqtt_issubscribed] */\r
+\r
+#endif /* ifndef IOT_MQTT_H_ */\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_agent.h\r
+ * @brief MQTT Agent Interface.\r
+ */\r
+\r
+#ifndef _AWS_MQTT_AGENT_H_\r
+#define _AWS_MQTT_AGENT_H_\r
+\r
+/* FreeRTOS includes. */\r
+#include "FreeRTOS.h"\r
+\r
+/* MQTT lib includes. */\r
+#include "iot_mqtt_lib.h"\r
+\r
+/* Library initialization definition include */\r
+#include "iot_lib_init.h"\r
+\r
+/**\r
+ * @brief Opaque handle to represent an MQTT client.\r
+ *\r
+ * The MQTT library is capable of creating multiple MQTT clients, maximum number of which\r
+ * is controlled by mqttconfigMAX_BROKERS macro. Each client is identified by an opaque\r
+ * handle which is returned by the MQTT_AGENT_Create API call and later used in all\r
+ * the subsequent API calls.\r
+ */\r
+typedef void * MQTTAgentHandle_t;\r
+\r
+/**\r
+ * @brief Return codes.\r
+ *\r
+ * Each API returns a value of this type.\r
+ */\r
+typedef enum\r
+{\r
+ eMQTTAgentSuccess, /**< The operation was successful. */\r
+ eMQTTAgentFailure, /**< The operation failed. */\r
+ eMQTTAgentTimeout, /**< The operation timed out. */\r
+ eMQTTAgentAPICalledFromCallback /**< The MQTT agent APIs must not be called from MQTT callbacks as callbacks run\r
+ * in the context of MQTT agent task and therefore can result in deadlock. This\r
+ * error code is returned if any MQTT agent API is invoked from any callback. */\r
+} MQTTAgentReturnCode_t;\r
+\r
+/**\r
+ * @brief Various events reported by the library in the callback.\r
+ *\r
+ * The user can register an optional callback with the MQTT library to\r
+ * get notified of various events including Publish messages received\r
+ * from the broker. This enum identifies the event received in the\r
+ * callback.\r
+ */\r
+typedef enum\r
+{\r
+ eMQTTAgentPublish, /**< A Publish message was received from the broker. */\r
+ eMQTTAgentDisconnect /**< The connection to the broker got disconnected. */\r
+} MQTTAgentEvent_t;\r
+\r
+/**\r
+ * @brief Passed by the library in the callback to inform the user of various events.\r
+ *\r
+ * If the user has registered a callback to get notified of various events, a pointer\r
+ * to this structure is passed in the callback function.\r
+ * @see MQTTAgentEvent_t.\r
+ */\r
+typedef struct MQTTAgentCallbackParams\r
+{\r
+ MQTTAgentEvent_t xMQTTEvent; /**< Type of the event received. */\r
+ /* This union is here for future support. */\r
+ union\r
+ {\r
+ MQTTPublishData_t xPublishData; /**< Publish data. Meaningful only in case of eMQTTAgentPublish event. */\r
+ } u;\r
+} MQTTAgentCallbackParams_t;\r
+\r
+/**\r
+ * @brief Signature of the callback registered by the user to get notified of various events.\r
+ *\r
+ * The user can register an optional callback to get notified of various events.\r
+ *\r
+ * @param[in] pvUserData The user data as provided in the connect parameters while connecting.\r
+ * @param[in] pxCallbackParams The event and related data.\r
+ *\r
+ * @return The return value is ignored in all other cases except publish (i.e. eMQTTAgentPublish\r
+ * event):\r
+ * 1. If pdTRUE is returned - The ownership of the buffer passed in the callback (xBuffer\r
+ * in MQTTPublishData_t) lies with the user.\r
+ * 2. If pdFALSE is returned - The ownership of the buffer passed in the callback (xBuffer\r
+ * in MQTTPublishData_t) remains with the library and it is recycled as soon as\r
+ * the callback returns.<br>\r
+ * The user should take the ownership of the buffer containing the received message from the\r
+ * broker by returning pdTRUE from the callback if the user wants to use the buffer after\r
+ * the callback is over. The user should return the buffer whenever done by calling the\r
+ * MQTT_AGENT_ReturnBuffer API.\r
+ *\r
+ * @see MQTTAgentCallbackParams_t.\r
+ */\r
+typedef BaseType_t ( * MQTTAgentCallback_t )( void * pvUserData,\r
+ const MQTTAgentCallbackParams_t * const pxCallbackParams );\r
+\r
+/**\r
+ * @brief Flags for the MQTT agent connect params.\r
+ */\r
+#define mqttagentURL_IS_IP_ADDRESS 0x00000001 /**< Set this bit in xFlags if the provided URL is an IP address. */\r
+#define mqttagentREQUIRE_TLS 0x00000002 /**< Set this bit in xFlags to use TLS. */\r
+#define mqttagentUSE_AWS_IOT_ALPN_443 0x00000004 /**< Set this bit in xFlags to use AWS IoT support for MQTT over TLS port 443. */\r
+\r
+/**\r
+ * @brief Parameters passed to the MQTT_AGENT_Connect API.\r
+ */\r
+typedef struct MQTTAgentConnectParams\r
+{\r
+ const char * pcURL; /**< The URL of the MQTT broker to connect to. */\r
+ BaseType_t xFlags; /**< Flags to control the behavior of MQTT connect. */\r
+ BaseType_t xURLIsIPAddress; /**< Deprecated. Set the mqttagentURL_IS_IP_ADDRESS bit in xFlags instead. */\r
+ uint16_t usPort; /**< Port number at which MQTT broker is listening. This field is ignored if the mqttagentUSE_AWS_IOT_ALPN_443 flag is set. */\r
+ const uint8_t * pucClientId; /**< Client Identifier of the MQTT client. It should be unique per broker. */\r
+ uint16_t usClientIdLength; /**< The length of the client Id. */\r
+ BaseType_t xSecuredConnection; /**< Deprecated. Set the mqttagentREQUIRE_TLS bit in xFlags instead. */\r
+ void * pvUserData; /**< User data supplied back as it is in the callback. Can be NULL. */\r
+ MQTTAgentCallback_t pxCallback; /**< Callback used to report various events. In addition to other events, this callback is invoked for the publish\r
+ * messages received on the topics for which the user has not registered any subscription callback. Can be NULL. */\r
+ char * pcCertificate; /**< Certificate used for secure connection. Can be NULL. If it is NULL, the one specified in the aws_credential_keys.h is used. */\r
+ uint32_t ulCertificateSize; /**< Size of certificate used for secure connection. */\r
+} MQTTAgentConnectParams_t;\r
+\r
+/**\r
+ * @brief Parameters passed to the MQTT_AGENT_Subscribe API.\r
+ */\r
+typedef struct MQTTAgentSubscribeParams\r
+{\r
+ const uint8_t * pucTopic; /**< The topic to subscribe to. This can be a topic filter containing wild cards as permitted by the MQTT protocol. */\r
+ uint16_t usTopicLength; /**< The length of the topic. */\r
+ MQTTQoS_t xQoS; /**< Requested Quality of Service. */\r
+ #if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )\r
+ void * pvPublishCallbackContext; /**< Passed as it is in the publish callback. Can be NULL. */\r
+ MQTTPublishCallback_t pxPublishCallback; /**< Callback function to be called whenever a publish message is received on this topic or on a topic which matches this\r
+ * topic filter. If a publish message is received on a topic which matches more than one topic filters, the order in which\r
+ * the callbacks are invoked is undefined. This can be NULL if the user does not want to register a topic specific callback,\r
+ * in which case the generic callback ( if registered during connect ) is invoked. */\r
+ #endif /* mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT */\r
+} MQTTAgentSubscribeParams_t;\r
+\r
+/**\r
+ * @brief Parameters passed to the MQTT_AGENT_Unsubscribe API.\r
+ */\r
+typedef struct MQTTAgentUnsubscribeParams\r
+{\r
+ const uint8_t * pucTopic; /**< The topic to unsubscribe from. */\r
+ uint16_t usTopicLength; /**< The length of the topic. */\r
+} MQTTAgentUnsubscribeParams_t;\r
+\r
+/**\r
+ * @brief Parameters passed to the MQTT_AGENT_Publish API.\r
+ */\r
+typedef struct MQTTAgentPublishParams\r
+{\r
+ const uint8_t * pucTopic; /**< The topic string on which the message should be published. */\r
+ uint16_t usTopicLength; /**< The length of the topic. */\r
+ MQTTQoS_t xQoS; /**< Quality of Service (qos). */\r
+ const void * pvData; /**< The data to publish. This data is copied into the MQTT buffers and therefore the user can free the buffer after the MQTT_AGENT_Publish call returns. */\r
+ uint32_t ulDataLength; /**< Length of the data. */\r
+} MQTTAgentPublishParams_t;\r
+\r
+/**\r
+ * @brief MQTT library Init function.\r
+ *\r
+ * This function does general initialization and setup. It must be called once\r
+ * and only once before calling any other function.\r
+ *\r
+ * @return pdPASS if everything succeeds, pdFAIL otherwise.\r
+ */\r
+lib_initDECLARE_LIB_INIT( MQTT_AGENT_Init );\r
+\r
+/**\r
+ * @brief Creates a new MQTT client.\r
+ *\r
+ * The MQTT library is capable of creating multiple MQTT clients, maximum number of which\r
+ * is controlled by mqttconfigMAX_BROKERS macro. If mqttconfigMAX_BROKERS clients are already\r
+ * in use, this function will fail immediately. Otherwise a new client is setup and the handle\r
+ * to the created client is returned in the pxMQTTHandle parameter which should be used in all\r
+ * the subsequent API calls. Note that the returned handled is only valid if the return value\r
+ * of the API is eMQTTAgentSuccess.\r
+ *\r
+ * @param[out] pxMQTTHandle Output parameter to return the opaque client handle.\r
+ *\r
+ * @return eMQTTAgentSuccess if a new client is successfully created, otherwise an error code\r
+ * explaining the reason of the failure is returned.\r
+ */\r
+MQTTAgentReturnCode_t MQTT_AGENT_Create( MQTTAgentHandle_t * const pxMQTTHandle );\r
+\r
+/**\r
+ * @brief Deletes the already created MQTT client.\r
+ *\r
+ * This function just frees up the internal resources and does not disconnect. The user must\r
+ * call MQTT_AGENT_Disconnect API to make sure that the client is disconnected before\r
+ * deleting it.\r
+ *\r
+ * @param[in] xMQTTHandle The opaque handle as returned from MQTT_AGENT_Create.\r
+ *\r
+ * @return eMQTTAgentSuccess if the client is successfully deleted, otherwise an\r
+ * error code explaining the reason of the failure is returned.\r
+ */\r
+MQTTAgentReturnCode_t MQTT_AGENT_Delete( MQTTAgentHandle_t xMQTTHandle );\r
+\r
+/**\r
+ * @brief Establishes a connection with the MQTT broker.\r
+ *\r
+ * @note This function alters the calling task's notification state and value. If xTimeoutTicks\r
+ * is short the calling task's notification state and value may be updated after MQTT_AGENT_Connect()\r
+ * has returned.\r
+ *\r
+ * @param[in] xMQTTHandle The opaque handle as returned from MQTT_AGENT_Create.\r
+ * @param[in] pxConnectParams Connect parameters.\r
+ * @param[in] xTimeoutTicks Maximum time in ticks after which the operation should fail. Use pdMS_TO_TICKS\r
+ * macro to convert milliseconds to ticks.\r
+ *\r
+ * @return eMQTTAgentSuccess if the connect operation succeeds, otherwise an error code explaining the\r
+ * reason of the failure is returned.\r
+ */\r
+MQTTAgentReturnCode_t MQTT_AGENT_Connect( MQTTAgentHandle_t xMQTTHandle,\r
+ const MQTTAgentConnectParams_t * const pxConnectParams,\r
+ TickType_t xTimeoutTicks );\r
+\r
+/**\r
+ * @brief Disconnects the connection with the MQTT broker.\r
+ *\r
+ * @note This function alters the calling task's notification state and value. If xTimeoutTicks\r
+ * is short the calling task's notification state and value may be updated after MQTT_AGENT_Disconnect()\r
+ * has returned.\r
+ *\r
+ * @param[in] xMQTTHandle The opaque handle as returned from MQTT_AGENT_Create.\r
+ * @param[in] xTimeoutTicks Maximum time in ticks after which the operation should fail. Use pdMS_TO_TICKS\r
+ * macro to convert milliseconds to ticks.\r
+ *\r
+ * @return eMQTTAgentSuccess if the disconnect operation succeeds, otherwise an error code explaining\r
+ * the reason of the failure is returned.\r
+ */\r
+MQTTAgentReturnCode_t MQTT_AGENT_Disconnect( MQTTAgentHandle_t xMQTTHandle,\r
+ TickType_t xTimeoutTicks );\r
+\r
+/**\r
+ * @brief Subscribes to a given topic.\r
+ *\r
+ * @note This function alters the calling task's notification state and value. If xTimeoutTicks\r
+ * is short the calling task's notification state and value may be updated after MQTT_AGENT_Subscribe()\r
+ * has returned.\r
+ *\r
+ * Whenever a publish message is received on a topic, the registered callbacks are invoked\r
+ * in the following order:\r
+ * * If we have an exact matching entry in the subscription manager, the corresponding\r
+ * callback is invoked.\r
+ * * Then the wild card topic filters are checked for match and the corresponding callbacks\r
+ * are invoked for the ones which match the topic.\r
+ *\r
+ * @note If a publish message is received on a topic which matches more than one topic\r
+ * filters, the order in which the registered callbacks are invoked is undefined.\r
+ *\r
+ * @warning If the user takes the ownership of the MQTT buffer by returning eMQTTTrue from the\r
+ * callback, no further callbacks are invoked. The user should make sure not to take the ownership\r
+ * of the MQTT buffer if they want all the callbacks to get invoked. For example:\r
+ * * Subscriptions: a/b/c, a/b/#, a/b/+\r
+ * * Publish message received on topic: a/b/c --> First the callback corresponding to a/b/c\r
+ * subscription is invoked. Then the callbacks for topic filters a/b/# and a/b/+ are invoked\r
+ * in no particular order. If the user decides to take the ownership of the MQTT buffer in\r
+ * any of the callback by returning eMQTTTrue, no further callbacks are invoked.\r
+ *\r
+ * @param[in] xMQTTHandle The opaque handle as returned from MQTT_AGENT_Create.\r
+ * @param[in] pxSubscribeParams Subscribe parameters.\r
+ * @param[in] xTimeoutTicks Maximum time in ticks after which the operation should fail. Use pdMS_TO_TICKS\r
+ * macro to convert milliseconds to ticks.\r
+ *\r
+ * @return eMQTTAgentSuccess if the subscribe operation succeeds, otherwise an error code explaining\r
+ * the reason of the failure is returned.\r
+ */\r
+MQTTAgentReturnCode_t MQTT_AGENT_Subscribe( MQTTAgentHandle_t xMQTTHandle,\r
+ const MQTTAgentSubscribeParams_t * const pxSubscribeParams,\r
+ TickType_t xTimeoutTicks );\r
+\r
+/**\r
+ * @brief Unsubscribes from a given topic.\r
+ *\r
+ * @note This function alters the calling task's notification state and value. If xTimeoutTicks\r
+ * is short the calling task's notification state and value may be updated after MQTT_AGENT_Unsubscribe()\r
+ * has returned.\r
+ *\r
+ * @param[in] xMQTTHandle The opaque handle as returned from MQTT_AGENT_Create.\r
+ * @param[in] pxUnsubscribeParams Unsubscribe parameters.\r
+ * @param[in] xTimeoutTicks Maximum time in ticks after which the operation should fail. Use pdMS_TO_TICKS\r
+ * macro to convert milliseconds to ticks.\r
+ *\r
+ * @return eMQTTAgentSuccess if the unsubscribe operation succeeds, otherwise an error code explaining\r
+ * the reason of the failure is returned.\r
+ */\r
+MQTTAgentReturnCode_t MQTT_AGENT_Unsubscribe( MQTTAgentHandle_t xMQTTHandle,\r
+ const MQTTAgentUnsubscribeParams_t * const pxUnsubscribeParams,\r
+ TickType_t xTimeoutTicks );\r
+\r
+/**\r
+ * @brief Publishes a message to a given topic.\r
+ *\r
+ * @note This function alters the calling task's notification state and value. If xTimeoutTicks\r
+ * is short the calling task's notification state and value may be updated after MQTT_AGENT_Publish()\r
+ * has returned.\r
+ *\r
+ * @param[in] xMQTTHandle The opaque handle as returned from MQTT_AGENT_Create.\r
+ * @param[in] pxPublishParams Publish parameters.\r
+ * @param[in] xTimeoutTicks Maximum time in ticks after which the operation should fail. Use pdMS_TO_TICKS\r
+ * macro to convert milliseconds to ticks.\r
+ *\r
+ * @return eMQTTAgentSuccess if the publish operation succeeds, otherwise an error code explaining\r
+ * the reason of the failure is returned.\r
+ */\r
+MQTTAgentReturnCode_t MQTT_AGENT_Publish( MQTTAgentHandle_t xMQTTHandle,\r
+ const MQTTAgentPublishParams_t * const pxPublishParams,\r
+ TickType_t xTimeoutTicks );\r
+\r
+/**\r
+ * @brief Returns the buffer provided in the publish callback.\r
+ *\r
+ * When a publish message is received from the broker, the buffer containing the message\r
+ * is returned in the user supplied callback (xBuffer in MQTTPublishData_t) and the user\r
+ * can take the ownership by returning pdTRUE from the callback. The user should later\r
+ * return the buffer whenever done by calling the MQTT_AGENT_ReturnBuffer API.\r
+ *\r
+ * @param[in] xMQTTHandle The opaque handle as returned from MQTT_AGENT_Create.\r
+ * @param[in] xBufferHandle The buffer to return.\r
+ *\r
+ * @return eMQTTAgentSuccess if the return buffer operation succeeds, otherwise an error\r
+ * code explaining the reason of the failure is returned.\r
+ */\r
+MQTTAgentReturnCode_t MQTT_AGENT_ReturnBuffer( MQTTAgentHandle_t xMQTTHandle,\r
+ MQTTBufferHandle_t xBufferHandle );\r
+\r
+#endif /* _AWS_MQTT_AGENT_H_ */\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_agent_config_defaults.h\r
+ * @brief MQTT agent default config options.\r
+ *\r
+ * Ensures that the config options for MQTT agent are set to sensible\r
+ * default values if the user does not provide one.\r
+ */\r
+\r
+#ifndef _AWS_MQTT_AGENT_CONFIG_DEFAULTS_H_\r
+#define _AWS_MQTT_AGENT_CONFIG_DEFAULTS_H_\r
+\r
+/* FreeRTOS includes. */\r
+#include "FreeRTOS.h"\r
+#include "task.h"\r
+\r
+/**\r
+ * @brief Controls whether or not to report usage metrics to the\r
+ * AWS IoT broker.\r
+ *\r
+ * If mqttconfigENABLE_METRICS is set to 1, a string containing\r
+ * metric information will be included in the "username" field of\r
+ * the MQTT connect messages.\r
+ */\r
+#ifndef mqttconfigENABLE_METRICS\r
+ #define mqttconfigENABLE_METRICS ( 1 )\r
+#endif\r
+\r
+/**\r
+ * @brief The maximum time interval in seconds allowed to elapse between 2 consecutive\r
+ * control packets.\r
+ */\r
+#ifndef mqttconfigKEEP_ALIVE_INTERVAL_SECONDS\r
+ #define mqttconfigKEEP_ALIVE_INTERVAL_SECONDS ( 1200 )\r
+#endif\r
+\r
+/**\r
+ * @brief Defines the frequency at which the client should send Keep Alive messages.\r
+ *\r
+ * Even though the maximum time allowed between 2 consecutive control packets\r
+ * is defined by the mqttconfigKEEP_ALIVE_INTERVAL_SECONDS macro, the user\r
+ * can and should send Keep Alive messages at a slightly faster rate to ensure\r
+ * that the connection is not closed by the server because of network delays.\r
+ * This macro defines the interval of inactivity after which a keep alive messages\r
+ * is sent.\r
+ */\r
+#ifndef mqttconfigKEEP_ALIVE_ACTUAL_INTERVAL_TICKS\r
+ #define mqttconfigKEEP_ALIVE_ACTUAL_INTERVAL_TICKS ( 5000 )\r
+#endif\r
+\r
+/**\r
+ * @brief The maximum interval in ticks to wait for PINGRESP.\r
+ *\r
+ * If PINGRESP is not received within this much time after sending PINGREQ,\r
+ * the client assumes that the PINGREQ timed out.\r
+ */\r
+#ifndef mqttconfigKEEP_ALIVE_TIMEOUT_TICKS\r
+ #define mqttconfigKEEP_ALIVE_TIMEOUT_TICKS ( 1000 )\r
+#endif\r
+\r
+/**\r
+ * @brief The maximum time in ticks for which the MQTT task is permitted to block.\r
+ *\r
+ * The MQTT task blocks until the user initiates any action or until it receives\r
+ * any data from the broker. This macro controls the maximum time the MQTT task can\r
+ * block. It should be set to a small number for the platforms which do not have any\r
+ * mechanism to wake up the MQTT task whenever data is received on a connected socket.\r
+ * This ensures that the MQTT task keeps waking up frequently and processes the publish\r
+ * messages received from the broker, if any.\r
+ *\r
+ * If the platform's secure_sockets layer supports SOCKETS_SO_WAKEUP_CALLBACK i.e.\r
+ * the MQTT task can wake up whenever data is received on a connected socket, this\r
+ * value should be set to maximum value:\r
+ * #define #define mqttconfigMQTT_TASK_MAX_BLOCK_TICKS ( ~( ( uint32_t ) 0 ) )\r
+ *\r
+ * If the platform's secure_sockets layer does not support SOCKETS_SO_WAKEUP_CALLBACK\r
+ * i.e. the MQTT task cannot wake up whenever data is received on a connected socket,\r
+ * this value should be set to a small number:\r
+ * #define mqttconfigMQTT_TASK_MAX_BLOCK_TICKS ( 100 )\r
+ */\r
+#ifndef mqttconfigMQTT_TASK_MAX_BLOCK_TICKS\r
+ #error "mqttconfigMQTT_TASK_MAX_BLOCK_TICKS must be defined in iot_mqtt_agent_config.h."\r
+#endif\r
+\r
+/**\r
+ * @defgroup MQTTTask MQTT task configuration parameters.\r
+ */\r
+/** @{ */\r
+#ifndef mqttconfigMQTT_TASK_STACK_DEPTH\r
+ #define mqttconfigMQTT_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 4 )\r
+#endif\r
+\r
+#ifndef mqttconfigMQTT_TASK_PRIORITY\r
+ #define mqttconfigMQTT_TASK_PRIORITY ( tskIDLE_PRIORITY )\r
+#endif\r
+/** @} */\r
+\r
+/**\r
+ * @brief Maximum number of MQTT clients that can exist simultaneously.\r
+ */\r
+#ifndef mqttconfigMAX_BROKERS\r
+ #define mqttconfigMAX_BROKERS ( 1 )\r
+#endif\r
+\r
+/**\r
+ * @brief Maximum number of parallel operations per client.\r
+ */\r
+#ifndef mqttconfigMAX_PARALLEL_OPS\r
+ #define mqttconfigMAX_PARALLEL_OPS ( 5 )\r
+#endif\r
+\r
+/**\r
+ * @brief Time in milliseconds after which the TCP send operation should timeout.\r
+ */\r
+#ifndef mqttconfigTCP_SEND_TIMEOUT_MS\r
+ #define mqttconfigTCP_SEND_TIMEOUT_MS ( 2000 )\r
+#endif\r
+\r
+/**\r
+ * @brief Length of the buffer used to receive data.\r
+ */\r
+#ifndef mqttconfigRX_BUFFER_SIZE\r
+ #define mqttconfigRX_BUFFER_SIZE ( 1024 )\r
+#endif\r
+\r
+/**\r
+ * @defgroup BufferPoolInterface The functions used by the MQTT client to get and return buffers.\r
+ *\r
+ * The MQTT client needs buffers for both transmitting and receiving messages.\r
+ * Whenever it needs a buffer, it invokes mqttconfigGET_FREE_BUFFER_FXN function to get\r
+ * a buffer and after it is done it invokes mqttconfigRETURN_BUFFER_FXN to return the\r
+ * buffer. By default, BUFFERPOOL_GetFreeBuffer and BUFFERPOOL_ReturnBuffer functions are\r
+ * used to get and return buffers from the central buffer pool. The user can change the\r
+ * buffer management functions for MQTT client by defining mqttconfigGET_FREE_BUFFER_FXN\r
+ * and mqttconfigRETURN_BUFFER_FXN macros. The user should implement the two functions\r
+ * having signatures same as BUFFERPOOL_GetFreeBuffer and BUFFERPOOL_ReturnBuffer and then\r
+ * define the macros in BufferPoolConfig.h:\r
+ * @code\r
+ * uint8_t* UserDefined_GetFreeBuffer( uint32_t *pulBufferLength );\r
+ * void UserDefined_ReturnBuffer( uint8_t * const pucBuffer );\r
+ *\r
+ * #define mqttconfigGET_FREE_BUFFER_FXN UserDefined_GetFreeBuffer\r
+ * #define mqttconfigRETURN_BUFFER_FXN UserDefined_ReturnBuffer\r
+ * @endcode\r
+ */\r
+/** @{ */\r
+#ifndef mqttconfigGET_FREE_BUFFER_FXN\r
+ #define mqttconfigGET_FREE_BUFFER_FXN BUFFERPOOL_GetFreeBuffer\r
+#endif\r
+\r
+#ifndef mqttconfigRETURN_BUFFER_FXN\r
+ #define mqttconfigRETURN_BUFFER_FXN BUFFERPOOL_ReturnBuffer\r
+#endif\r
+/** @} */\r
+\r
+#endif /* _AWS_MQTT_AGENT_CONFIG_DEFAULTS_H_ */\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_config_defaults.h\r
+ * @brief MQTT default config options.\r
+ *\r
+ * Ensures that the config options for MQTT are set to sensible default\r
+ * values if the user does not provide one.\r
+ */\r
+\r
+#ifndef _AWS_MQTT_CONFIG_DEFAULTS_H_\r
+#define _AWS_MQTT_CONFIG_DEFAULTS_H_\r
+\r
+/**\r
+ * @brief Enable subscription management.\r
+ *\r
+ * Subscription management allows the user to register per subscription\r
+ * callback.\r
+ */\r
+#ifndef mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT\r
+ #define mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT ( 1 )\r
+#endif\r
+\r
+/**\r
+ * @brief Maximum length of the topic which can be stored in subscription\r
+ * manager.\r
+ *\r
+ * If the user has enabled subscription management (by defining the macro\r
+ * mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT to 1), then this macro must be defined\r
+ * to accommodate the maximum length topic which the user is going to subscribe.\r
+ * The subscribe operation will fail if the user tries to subscribe to a topic\r
+ * of length more than the maximum specified here.\r
+ */\r
+#ifndef mqttconfigSUBSCRIPTION_MANAGER_MAX_TOPIC_LENGTH\r
+ #define mqttconfigSUBSCRIPTION_MANAGER_MAX_TOPIC_LENGTH ( 128 )\r
+#endif\r
+\r
+/**\r
+ * @brief Maximum number of subscriptions which can be stored in subscription\r
+ * manager.\r
+ *\r
+ * If the user has enabled subscription management (by defining the macro\r
+ * mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT to 1), then this macro must be defined\r
+ * to the maximum number of topics which the user is going to subscribe\r
+ * simultaneously. The subscribe operation will fail is the user tries to\r
+ * subscribe to more topics than the maximum specified here.\r
+ */\r
+#ifndef mqttconfigSUBSCRIPTION_MANAGER_MAX_SUBSCRIPTIONS\r
+ #define mqttconfigSUBSCRIPTION_MANAGER_MAX_SUBSCRIPTIONS ( 8 )\r
+#endif\r
+\r
+/**\r
+ * @brief Define mqttconfigASSERT to enable asserts.\r
+ *\r
+ * mqttconfigASSERT should be defined to match the semantics of standard\r
+ * C assert() macro i.e. an assertion should trigger if the parameter\r
+ * passed is zero. If the standard C assert is available, the user might\r
+ * do the following:\r
+ * @code\r
+ * #define mqttconfigASSERT( x ) assert( x )\r
+ * @endcode\r
+ *\r
+ * Otherwise, a user can choose to implement a function which should be\r
+ * called when an assertion triggers and then define the mqttconfigASSERT\r
+ * to that function:\r
+ * @code\r
+ * extern void vAssertCalled( const char *pcFile, uint32_t ulLine );\r
+ * #define mqttconfigASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )\r
+ * @endcode\r
+ */\r
+#ifndef mqttconfigASSERT\r
+ #define mqttconfigASSERT( x )\r
+#endif\r
+\r
+/**\r
+ * @brief Define mqttconfigENABLE_DEBUG_LOGS macro to 1 for enabling debug logs.\r
+ *\r
+ * If you choose to enable debug logs, the following function must be implemented\r
+ * which is called to print logs:\r
+ * @code\r
+ * void vLoggingPrintf( const char *pcFormatString, ... );\r
+ * @endcode\r
+ */\r
+#if ( mqttconfigENABLE_DEBUG_LOGS == 1 )\r
+ extern void vLoggingPrintf( const char * pcFormatString,\r
+ ... );\r
+ #define mqttconfigDEBUG_LOG( x ) vLoggingPrintf x\r
+#else\r
+ #define mqttconfigDEBUG_LOG( x )\r
+#endif\r
+\r
+#endif /* _AWS_MQTT_CONFIG_DEFAULTS_H_ */\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_lib.h\r
+ * @brief MQTT Core Library interface.\r
+ */\r
+\r
+#ifndef _AWS_MQTT_LIB_H_\r
+#define _AWS_MQTT_LIB_H_\r
+\r
+/* This ifndef enables the core MQTT library to be used without\r
+ * providing MQTTConfig.h. All the config values in this case are\r
+ * taken from MQTTConfigDefaults.h. */\r
+#ifndef mqttDO_NOT_USE_CUSTOM_CONFIG\r
+ #include "aws_mqtt_config.h"\r
+#endif\r
+#include "iot_mqtt_config_defaults.h"\r
+\r
+#include "iot_doubly_linked_list.h"\r
+\r
+ /**\r
+ * @brief Opaque handle to represent an MQTT buffer.\r
+ */\r
+typedef void * MQTTBufferHandle_t;\r
+\r
+/**\r
+ * @brief Boolean type.\r
+ */\r
+typedef enum\r
+{\r
+ eMQTTFalse = 0, /**< Boolean False. */\r
+ eMQTTTrue = 1 /**< Boolean True. */\r
+} MQTTBool_t;\r
+\r
+/**\r
+ * @brief Quality of Service (qos).\r
+ */\r
+typedef enum\r
+{\r
+ eMQTTQoS0 = 0, /**< Quality of Service 0 - Fire and Forget. No ACK. */\r
+ eMQTTQoS1 = 1, /**< Quality of Service 1 - Wait till ACK or Timeout. */\r
+ eMQTTQoS2 = 2 /**< Quality of Service 2 - Not supported. */\r
+} MQTTQoS_t;\r
+\r
+/**\r
+ * @brief The data sent by the MQTT library in the user supplied callback\r
+ * when a publish message from the broker is received.\r
+ */\r
+typedef struct MQTTPublishData\r
+{\r
+ MQTTQoS_t xQos; /**< Quality of Service (qos). */\r
+ const uint8_t * pucTopic; /**< The topic on which the message is received. */\r
+ uint16_t usTopicLength; /**< Length of the topic. */\r
+ const void * pvData; /**< The received message. */\r
+ uint32_t ulDataLength; /**< Length of the message. */\r
+ MQTTBufferHandle_t xBuffer; /**< The buffer containing the whole MQTT message. Both pcTopic and pvData are pointers to the locations in this buffer. */\r
+} MQTTPublishData_t;\r
+\r
+/**\r
+ * @brief Signature of the user supplied topic specific publish callback which gets called\r
+ * whenever a publish message is received on the topic this callback is registered for.\r
+ *\r
+ * The user can choose to register this optional topic specific callback while subscribing to\r
+ * a topic. Whenever a publish message is received on the topic, this callback is invoked. If\r
+ * the user chooses not to enable subscription management or chooses not to register a topic\r
+ * specific callback, the generic callback supplied during Init is invoked.\r
+ *\r
+ * @param[in] pvPublishCallbackContext The callback context as supplied by the user in the\r
+ * subscribe parameters.\r
+ * @param[in] pxPublishData The publish data.\r
+ *\r
+ * @return The return value is interpreted as follows:\r
+ * 1. If eMQTTTrue is returned - the ownership of the buffer passed in the callback (xBuffer\r
+ * in MQTTPublishData_t) lies with the user.\r
+ * 2. If eMQTTFalse is returned - the ownership of the buffer passed in the callback (xBuffer\r
+ * in MQTTPublishData_t) remains with the library and it is recycled as soon as the callback\r
+ * returns.<br>\r
+ * The user should take the ownership of the buffer containing the received message from the\r
+ * broker by returning eMQTTTrue from the callback if the user wants to use the buffer after\r
+ * the callback is over. The user should return the buffer whenever done by calling the\r
+ * MQTT_ReturnBuffer API.\r
+ */\r
+#if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )\r
+\r
+ typedef MQTTBool_t ( * MQTTPublishCallback_t )( void * pvPublishCallbackContext,\r
+ const MQTTPublishData_t * const pxPublishData );\r
+\r
+#endif /* mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT */\r
+\r
+#endif /* _AWS_MQTT_LIB_H_ */\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_types.h\r
+ * @brief Types of the MQTT library.\r
+ */\r
+\r
+#ifndef IOT_MQTT_TYPES_H_\r
+#define IOT_MQTT_TYPES_H_\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Standard includes. */\r
+#include <stdbool.h>\r
+#include <stdint.h>\r
+#include <stddef.h>\r
+\r
+/* Type includes. */\r
+#include "types/iot_platform_types.h"\r
+#include "types/iot_taskpool_types.h"\r
+\r
+/* Platform network include. */\r
+#include "platform/iot_network.h"\r
+\r
+/*---------------------------- MQTT handle types ----------------------------*/\r
+\r
+/**\r
+ * @handles{mqtt,MQTT library}\r
+ */\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_handles\r
+ * @brief Opaque handle of an MQTT connection.\r
+ *\r
+ * This type identifies an MQTT connection, which is valid after a successful call\r
+ * to @ref mqtt_function_connect. A variable of this type is passed as the first\r
+ * argument to [MQTT library functions](@ref mqtt_functions) to identify which\r
+ * connection that function acts on.\r
+ *\r
+ * A call to @ref mqtt_function_disconnect makes a connection handle invalid. Once\r
+ * @ref mqtt_function_disconnect returns, the connection handle should no longer\r
+ * be used.\r
+ *\r
+ * @initializer{IotMqttConnection_t,IOT_MQTT_CONNECTION_INITIALIZER}\r
+ */\r
+typedef struct _mqttConnection * IotMqttConnection_t;\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_handles\r
+ * @brief Opaque handle that references an in-progress MQTT operation.\r
+ *\r
+ * Set as an output parameter of @ref mqtt_function_publish, @ref mqtt_function_subscribe,\r
+ * and @ref mqtt_function_unsubscribe. These functions queue an MQTT operation; the result\r
+ * of the operation is unknown until a response from the MQTT server is received. Therefore,\r
+ * this handle serves as a reference to MQTT operations awaiting MQTT server response.\r
+ *\r
+ * This reference will be valid from the successful return of @ref mqtt_function_publish,\r
+ * @ref mqtt_function_subscribe, or @ref mqtt_function_unsubscribe. The reference becomes\r
+ * invalid once the [completion callback](@ref IotMqttCallbackInfo_t) is invoked, or\r
+ * @ref mqtt_function_wait returns.\r
+ *\r
+ * @initializer{IotMqttOperation_t,IOT_MQTT_OPERATION_INITIALIZER}\r
+ *\r
+ * @see @ref mqtt_function_wait and #IOT_MQTT_FLAG_WAITABLE for waiting on a reference.\r
+ * #IotMqttCallbackInfo_t and #IotMqttCallbackParam_t for an asynchronous notification\r
+ * of completion.\r
+ */\r
+typedef struct _mqttOperation * IotMqttOperation_t;\r
+\r
+/*-------------------------- MQTT enumerated types --------------------------*/\r
+\r
+/**\r
+ * @enums{mqtt,MQTT library}\r
+ */\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_enums\r
+ * @brief Return codes of [MQTT functions](@ref mqtt_functions).\r
+ *\r
+ * The function @ref mqtt_function_strerror can be used to get a return code's\r
+ * description.\r
+ */\r
+typedef enum IotMqttError\r
+{\r
+ /**\r
+ * @brief MQTT operation completed successfully.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_connect\r
+ * - @ref mqtt_function_publish with QoS 0 parameter\r
+ * - @ref mqtt_function_wait\r
+ * - @ref mqtt_function_timedsubscribe\r
+ * - @ref mqtt_function_timedunsubscribe\r
+ * - @ref mqtt_function_timedpublish\r
+ *\r
+ * Will also be the value of an operation completion callback's\r
+ * #IotMqttCallbackParam_t.result when successful.\r
+ */\r
+ IOT_MQTT_SUCCESS = 0,\r
+\r
+ /**\r
+ * @brief MQTT operation queued, awaiting result.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_subscribe\r
+ * - @ref mqtt_function_unsubscribe\r
+ * - @ref mqtt_function_publish with QoS 1 parameter\r
+ */\r
+ IOT_MQTT_STATUS_PENDING,\r
+\r
+ /**\r
+ * @brief Initialization failed.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_init\r
+ */\r
+ IOT_MQTT_INIT_FAILED,\r
+\r
+ /**\r
+ * @brief At least one parameter is invalid.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_connect\r
+ * - @ref mqtt_function_subscribe and @ref mqtt_function_timedsubscribe\r
+ * - @ref mqtt_function_unsubscribe and @ref mqtt_function_timedunsubscribe\r
+ * - @ref mqtt_function_publish and @ref mqtt_function_timedpublish\r
+ * - @ref mqtt_function_wait\r
+ */\r
+ IOT_MQTT_BAD_PARAMETER,\r
+\r
+ /**\r
+ * @brief MQTT operation failed because of memory allocation failure.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_connect\r
+ * - @ref mqtt_function_subscribe and @ref mqtt_function_timedsubscribe\r
+ * - @ref mqtt_function_unsubscribe and @ref mqtt_function_timedunsubscribe\r
+ * - @ref mqtt_function_publish and @ref mqtt_function_timedpublish\r
+ */\r
+ IOT_MQTT_NO_MEMORY,\r
+\r
+ /**\r
+ * @brief MQTT operation failed because the network was unusable.\r
+ *\r
+ * This return value may indicate that the network is disconnected.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_connect\r
+ * - @ref mqtt_function_wait\r
+ * - @ref mqtt_function_timedsubscribe\r
+ * - @ref mqtt_function_timedunsubscribe\r
+ * - @ref mqtt_function_timedpublish\r
+ *\r
+ * May also be the value of an operation completion callback's\r
+ * #IotMqttCallbackParam_t.result.\r
+ */\r
+ IOT_MQTT_NETWORK_ERROR,\r
+\r
+ /**\r
+ * @brief MQTT operation could not be scheduled, i.e. enqueued for sending.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_connect\r
+ * - @ref mqtt_function_subscribe and @ref mqtt_function_timedsubscribe\r
+ * - @ref mqtt_function_unsubscribe and @ref mqtt_function_timedunsubscribe\r
+ * - @ref mqtt_function_publish and @ref mqtt_function_timedpublish\r
+ */\r
+ IOT_MQTT_SCHEDULING_ERROR,\r
+\r
+ /**\r
+ * @brief MQTT response packet received from the network is malformed.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_connect\r
+ * - @ref mqtt_function_wait\r
+ * - @ref mqtt_function_timedsubscribe\r
+ * - @ref mqtt_function_timedunsubscribe\r
+ * - @ref mqtt_function_timedpublish\r
+ *\r
+ * May also be the value of an operation completion callback's\r
+ * #IotMqttCallbackParam_t.result.\r
+ *\r
+ * @note If this value is received, the network connection has been closed.\r
+ */\r
+ IOT_MQTT_BAD_RESPONSE,\r
+\r
+ /**\r
+ * @brief A blocking MQTT operation timed out.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_connect\r
+ * - @ref mqtt_function_wait\r
+ * - @ref mqtt_function_timedsubscribe\r
+ * - @ref mqtt_function_timedunsubscribe\r
+ * - @ref mqtt_function_timedpublish\r
+ */\r
+ IOT_MQTT_TIMEOUT,\r
+\r
+ /**\r
+ * @brief A CONNECT or at least one subscription was refused by the server.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_connect\r
+ * - @ref mqtt_function_wait, but only when its #IotMqttOperation_t parameter\r
+ * is associated with a SUBSCRIBE operation.\r
+ * - @ref mqtt_function_timedsubscribe\r
+ *\r
+ * May also be the value of an operation completion callback's\r
+ * #IotMqttCallbackParam_t.result for a SUBSCRIBE.\r
+ *\r
+ * @note If this value is returned and multiple subscriptions were passed to\r
+ * @ref mqtt_function_subscribe (or @ref mqtt_function_timedsubscribe), it's\r
+ * still possible that some of the subscriptions succeeded. This value only\r
+ * signifies that AT LEAST ONE subscription was rejected. The function @ref\r
+ * mqtt_function_issubscribed can be used to determine which subscriptions\r
+ * were accepted or rejected.\r
+ */\r
+ IOT_MQTT_SERVER_REFUSED,\r
+\r
+ /**\r
+ * @brief A QoS 1 PUBLISH received no response and [the retry limit]\r
+ * (#IotMqttPublishInfo_t.retryLimit) was reached.\r
+ *\r
+ * Functions that may return this value:\r
+ * - @ref mqtt_function_wait, but only when its #IotMqttOperation_t parameter\r
+ * is associated with a QoS 1 PUBLISH operation\r
+ * - @ref mqtt_function_timedpublish\r
+ *\r
+ * May also be the value of an operation completion callback's\r
+ * #IotMqttCallbackParam_t.result for a QoS 1 PUBLISH.\r
+ */\r
+ IOT_MQTT_RETRY_NO_RESPONSE\r
+} IotMqttError_t;\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_enums\r
+ * @brief Types of MQTT operations.\r
+ *\r
+ * The function @ref mqtt_function_operationtype can be used to get an operation\r
+ * type's description.\r
+ */\r
+typedef enum IotMqttOperationType\r
+{\r
+ IOT_MQTT_CONNECT, /**< Client-to-server CONNECT. */\r
+ IOT_MQTT_PUBLISH_TO_SERVER, /**< Client-to-server PUBLISH. */\r
+ IOT_MQTT_PUBACK, /**< Client-to-server PUBACK. */\r
+ IOT_MQTT_SUBSCRIBE, /**< Client-to-server SUBSCRIBE. */\r
+ IOT_MQTT_UNSUBSCRIBE, /**< Client-to-server UNSUBSCRIBE. */\r
+ IOT_MQTT_PINGREQ, /**< Client-to-server PINGREQ. */\r
+ IOT_MQTT_DISCONNECT /**< Client-to-server DISCONNECT. */\r
+} IotMqttOperationType_t;\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_enums\r
+ * @brief Quality of service levels for MQTT PUBLISH messages.\r
+ *\r
+ * All MQTT PUBLISH messages, including Last Will and Testament and messages\r
+ * received on subscription filters, have an associated <i>Quality of Service</i>,\r
+ * which defines any delivery guarantees for that message.\r
+ * - QoS 0 messages will be delivered at most once. This is a "best effort"\r
+ * transmission with no retransmissions.\r
+ * - QoS 1 messages will be delivered at least once. See #IotMqttPublishInfo_t\r
+ * for the retransmission strategy this library uses to redeliver messages\r
+ * assumed to be lost.\r
+ *\r
+ * @attention QoS 2 is not supported by this library and should not be used.\r
+ */\r
+typedef enum IotMqttQos\r
+{\r
+ IOT_MQTT_QOS_0 = 0, /**< Delivery at most once. */\r
+ IOT_MQTT_QOS_1 = 1, /**< Delivery at least once. See #IotMqttPublishInfo_t for client-side retry strategy. */\r
+ IOT_MQTT_QOS_2 = 2 /**< Delivery exactly once. Unsupported, but enumerated for completeness. */\r
+} IotMqttQos_t;\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_enums\r
+ * @brief The reason that an MQTT connection (and its associated network connection)\r
+ * was disconnected.\r
+ *\r
+ * When an MQTT connection is closed, its associated [disconnect callback]\r
+ * (@ref IotMqttNetworkInfo_t::disconnectCallback) will be invoked. This type\r
+ * is passed inside of an #IotMqttCallbackParam_t to provide a reason for the\r
+ * disconnect.\r
+ */\r
+typedef enum IotMqttDisconnectReason\r
+{\r
+ IOT_MQTT_DISCONNECT_CALLED, /**< @ref mqtt_function_disconnect was invoked. */\r
+ IOT_MQTT_BAD_PACKET_RECEIVED, /**< An invalid packet was received from the network. */\r
+ IOT_MQTT_KEEP_ALIVE_TIMEOUT /**< Keep-alive response was not received within @ref IOT_MQTT_RESPONSE_WAIT_MS. */\r
+} IotMqttDisconnectReason_t;\r
+\r
+/*------------------------- MQTT parameter structs --------------------------*/\r
+\r
+/**\r
+ * @paramstructs{mqtt,MQTT}\r
+ */\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_paramstructs\r
+ * @brief Information on a PUBLISH message.\r
+ *\r
+ * @paramfor @ref mqtt_function_connect, @ref mqtt_function_publish\r
+ *\r
+ * Passed to @ref mqtt_function_publish as the message to publish and @ref\r
+ * mqtt_function_connect as the Last Will and Testament (LWT) message.\r
+ *\r
+ * @initializer{IotMqttPublishInfo_t,IOT_MQTT_PUBLISH_INFO_INITIALIZER}\r
+ *\r
+ * #IotMqttPublishInfo_t.retryMs and #IotMqttPublishInfo_t.retryLimit are only\r
+ * relevant to QoS 1 PUBLISH messages. They are ignored for QoS 0 PUBLISH\r
+ * messages and LWT messages. These members control retransmissions of QoS 1\r
+ * messages under the following rules:\r
+ * - Retransmission is disabled when #IotMqttPublishInfo_t.retryLimit is 0.\r
+ * After sending the PUBLISH, the library will wait indefinitely for a PUBACK.\r
+ * - If #IotMqttPublishInfo_t.retryLimit is greater than 0, then QoS 1 publishes\r
+ * that do not receive a PUBACK within #IotMqttPublishInfo_t.retryMs will be\r
+ * retransmitted, up to #IotMqttPublishInfo_t.retryLimit times.\r
+ *\r
+ * Retransmission follows a truncated exponential backoff strategy. The constant\r
+ * @ref IOT_MQTT_RETRY_MS_CEILING controls the maximum time between retransmissions.\r
+ *\r
+ * After #IotMqttPublishInfo_t.retryLimit retransmissions are sent, the MQTT\r
+ * library will wait @ref IOT_MQTT_RESPONSE_WAIT_MS before a final check\r
+ * for a PUBACK. If no PUBACK was received within this time, the QoS 1 PUBLISH\r
+ * fails with the code #IOT_MQTT_RETRY_NO_RESPONSE.\r
+ *\r
+ * @note The lengths of the strings in this struct should not include the NULL\r
+ * terminator. Strings in this struct do not need to be NULL-terminated.\r
+ *\r
+ * @note The AWS IoT MQTT server does not support the DUP bit. When\r
+ * [using this library with the AWS IoT MQTT server](@ref IotMqttConnectInfo_t.awsIotMqttMode),\r
+ * retransmissions will instead be sent with a new packet identifier in the PUBLISH\r
+ * packet. This is a nonstandard workaround. Note that this workaround has some\r
+ * flaws, including the following:\r
+ * - The previous packet identifier is forgotten, so if a PUBACK arrives for that\r
+ * packet identifier, it will be ignored. On an exceptionally busy network, this\r
+ * may cause excessive retransmissions when too many PUBACKS arrive after the\r
+ * PUBLISH packet identifier is changed. However, the exponential backoff\r
+ * retransmission strategy should mitigate this problem.\r
+ * - Log messages will be printed using the new packet identifier; the old packet\r
+ * identifier is not saved.\r
+ *\r
+ * <b>Example</b>\r
+ *\r
+ * Consider a situation where\r
+ * - @ref IOT_MQTT_RETRY_MS_CEILING is 60000\r
+ * - #IotMqttPublishInfo_t.retryMs is 2000\r
+ * - #IotMqttPublishInfo_t.retryLimit is 20\r
+ *\r
+ * A PUBLISH message will be retransmitted at the following times after the initial\r
+ * transmission if no PUBACK is received:\r
+ * - 2000 ms (2000 ms after previous transmission)\r
+ * - 6000 ms (4000 ms after previous transmission)\r
+ * - 14000 ms (8000 ms after previous transmission)\r
+ * - 30000 ms (16000 ms after previous transmission)\r
+ * - 62000 ms (32000 ms after previous transmission)\r
+ * - 122000 ms, 182000 ms, 242000 ms... (every 60000 ms until 20 transmissions have been sent)\r
+ *\r
+ * After the 20th retransmission, the MQTT library will wait\r
+ * @ref IOT_MQTT_RESPONSE_WAIT_MS before checking a final time for a PUBACK.\r
+ */\r
+typedef struct IotMqttPublishInfo\r
+{\r
+ IotMqttQos_t qos; /**< @brief QoS of message. Must be 0 or 1. */\r
+ bool retain; /**< @brief MQTT message retain flag. */\r
+\r
+ const char * pTopicName; /**< @brief Topic name of PUBLISH. */\r
+ uint16_t topicNameLength; /**< @brief Length of #IotMqttPublishInfo_t.pTopicName. */\r
+\r
+ const void * pPayload; /**< @brief Payload of PUBLISH. */\r
+ size_t payloadLength; /**< @brief Length of #IotMqttPublishInfo_t.pPayload. For LWT messages, this is limited to 65535. */\r
+\r
+ uint32_t retryMs; /**< @brief If no response is received within this time, the message is retransmitted. */\r
+ uint32_t retryLimit; /**< @brief How many times to attempt retransmission. */\r
+} IotMqttPublishInfo_t;\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_paramstructs\r
+ * @brief Parameter to an MQTT callback function.\r
+ *\r
+ * @paramfor MQTT callback functions\r
+ *\r
+ * The MQTT library passes this struct to registered callback whenever an\r
+ * operation completes, a message is received on a topic filter, or an MQTT\r
+ * connection is disconnected.\r
+ *\r
+ * The members of this struct are different based on the callback trigger. If the\r
+ * callback function was triggered for completed operation, the `operation`\r
+ * member is valid. Otherwise, if the callback was triggered because of a\r
+ * server-to-client PUBLISH, the `message` member is valid. Finally, if the callback\r
+ * was triggered because of a disconnect, the `disconnectReason` member is valid.\r
+ *\r
+ * For an incoming PUBLISH, the `message.pTopicFilter` parameter provides the\r
+ * subscription topic filter that matched the topic name in the PUBLISH. Because\r
+ * topic filters may use MQTT wildcards, the topic filter may be different from the\r
+ * topic name. This pointer must be treated as read-only; the topic filter must not\r
+ * be modified. Additionally, the topic filter may go out of scope as soon as the\r
+ * callback function returns, so it must be copied if it is needed at a later time.\r
+ *\r
+ * @attention Any pointers in this callback parameter may be freed as soon as\r
+ * the [callback function](@ref IotMqttCallbackInfo_t.function) returns.\r
+ * Therefore, data must be copied if it is needed after the callback function\r
+ * returns.\r
+ * @attention The MQTT library may set strings that are not NULL-terminated.\r
+ *\r
+ * @see #IotMqttCallbackInfo_t for the signature of a callback function.\r
+ */\r
+typedef struct IotMqttCallbackParam\r
+{\r
+ /**\r
+ * @brief The MQTT connection associated with this completed operation,\r
+ * incoming PUBLISH, or disconnect.\r
+ *\r
+ * [MQTT API functions](@ref mqtt_functions) are safe to call from a callback\r
+ * for completed operations or incoming PUBLISH messages. However, blocking\r
+ * function calls (including @ref mqtt_function_wait) are not recommended\r
+ * (though still safe). Do not call any API functions from a disconnect\r
+ * callback.\r
+ */\r
+ IotMqttConnection_t mqttConnection;\r
+\r
+ union\r
+ {\r
+ /* Valid for completed operations. */\r
+ struct\r
+ {\r
+ IotMqttOperationType_t type; /**< @brief Type of operation that completed. */\r
+ IotMqttOperation_t reference; /**< @brief Reference to the operation that completed. */\r
+ IotMqttError_t result; /**< @brief Result of operation, e.g. succeeded or failed. */\r
+ } operation;\r
+\r
+ /* Valid for incoming PUBLISH messages. */\r
+ struct\r
+ {\r
+ const char * pTopicFilter; /**< @brief Topic filter that matched the message. */\r
+ uint16_t topicFilterLength; /**< @brief Length of `pTopicFilter`. */\r
+ IotMqttPublishInfo_t info; /**< @brief PUBLISH message received from the server. */\r
+ } message;\r
+\r
+ /* Valid when a connection is disconnected. */\r
+ IotMqttDisconnectReason_t disconnectReason; /**< @brief Why the MQTT connection was disconnected. */\r
+ } u; /**< @brief Valid member depends on callback type. */\r
+} IotMqttCallbackParam_t;\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_paramstructs\r
+ * @brief Information on a user-provided MQTT callback function.\r
+ *\r
+ * @paramfor @ref mqtt_function_subscribe, @ref mqtt_function_unsubscribe,\r
+ * and @ref mqtt_function_publish. Cannot be used with #IOT_MQTT_FLAG_WAITABLE.\r
+ *\r
+ * Provides a function to be invoked when an operation completes or when a\r
+ * server-to-client PUBLISH is received.\r
+ *\r
+ * @initializer{IotMqttCallbackInfo_t,IOT_MQTT_CALLBACK_INFO_INITIALIZER}\r
+ *\r
+ * Below is an example for receiving an asynchronous notification on operation\r
+ * completion. See @ref mqtt_function_subscribe for an example of using this struct\r
+ * with for incoming PUBLISH messages.\r
+ *\r
+ * @code{c}\r
+ * // Operation completion callback.\r
+ * void operationComplete( void * pArgument, IotMqttCallbackParam_t * pOperation );\r
+ *\r
+ * // Callback information.\r
+ * IotMqttCallbackInfo_t callbackInfo = IOT_MQTT_CALLBACK_INFO_INITIALIZER;\r
+ * callbackInfo.function = operationComplete;\r
+ *\r
+ * // Operation to wait for.\r
+ * IotMqttError_t result = IotMqtt_Publish( &mqttConnection,\r
+ * &publishInfo,\r
+ * 0,\r
+ * &callbackInfo,\r
+ * &reference );\r
+ *\r
+ * // Publish should have returned IOT_MQTT_STATUS_PENDING. Once a response\r
+ * // is received, operationComplete is executed with the actual status passed\r
+ * // in pOperation.\r
+ * @endcode\r
+ */\r
+typedef struct IotMqttCallbackInfo\r
+{\r
+ void * pCallbackContext; /**< @brief The first parameter to pass to the callback function to provide context. */\r
+\r
+ /**\r
+ * @brief User-provided callback function signature.\r
+ *\r
+ * @param[in] void * #IotMqttCallbackInfo_t.pCallbackContext\r
+ * @param[in] IotMqttCallbackParam_t * Details on the outcome of the MQTT operation\r
+ * or an incoming MQTT PUBLISH.\r
+ *\r
+ * @see #IotMqttCallbackParam_t for more information on the second parameter.\r
+ */\r
+ void ( * function )( void *,\r
+ IotMqttCallbackParam_t * );\r
+} IotMqttCallbackInfo_t;\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_paramstructs\r
+ * @brief Information on an MQTT subscription.\r
+ *\r
+ * @paramfor @ref mqtt_function_subscribe, @ref mqtt_function_unsubscribe\r
+ *\r
+ * An array of these is passed to @ref mqtt_function_subscribe and @ref\r
+ * mqtt_function_unsubscribe. However, #IotMqttSubscription_t.callback and\r
+ * and #IotMqttSubscription_t.qos are ignored by @ref mqtt_function_unsubscribe.\r
+ *\r
+ * @initializer{IotMqttSubscription_t,IOT_MQTT_SUBSCRIPTION_INITIALIZER}\r
+ *\r
+ * @note The lengths of the strings in this struct should not include the NULL\r
+ * terminator. Strings in this struct do not need to be NULL-terminated.\r
+ * @see #IotMqttCallbackInfo_t for details on setting a callback function.\r
+ */\r
+typedef struct IotMqttSubscription\r
+{\r
+ /**\r
+ * @brief QoS of messages delivered on subscription.\r
+ *\r
+ * Must be `0` or `1`. Ignored by @ref mqtt_function_unsubscribe.\r
+ */\r
+ IotMqttQos_t qos;\r
+\r
+ const char * pTopicFilter; /**< @brief Topic filter of subscription. */\r
+ uint16_t topicFilterLength; /**< @brief Length of #IotMqttSubscription_t.pTopicFilter. */\r
+\r
+ /**\r
+ * @brief Callback to invoke when a message is received.\r
+ *\r
+ * See #IotMqttCallbackInfo_t. Ignored by @ref mqtt_function_unsubscribe.\r
+ */\r
+ IotMqttCallbackInfo_t callback;\r
+} IotMqttSubscription_t;\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_paramstructs\r
+ * @brief Information on a new MQTT connection.\r
+ *\r
+ * @paramfor @ref mqtt_function_connect\r
+ *\r
+ * Passed as an argument to @ref mqtt_function_connect. Most members of this struct\r
+ * correspond to the content of an [MQTT CONNECT packet.]\r
+ * (http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/csprd02/mqtt-v3.1.1-csprd02.html#_Toc385349764)\r
+ *\r
+ * @initializer{IotMqttConnectInfo_t,IOT_MQTT_CONNECT_INFO_INITIALIZER}\r
+ *\r
+ * @note The lengths of the strings in this struct should not include the NULL\r
+ * terminator. Strings in this struct do not need to be NULL-terminated.\r
+ */\r
+typedef struct IotMqttConnectInfo\r
+{\r
+ /**\r
+ * @brief Specifies if this MQTT connection is to an AWS IoT MQTT server.\r
+ *\r
+ * The AWS IoT MQTT broker [differs somewhat from the MQTT specification.]\r
+ * (https://docs.aws.amazon.com/iot/latest/developerguide/mqtt.html)\r
+ * When this member is `true`, the MQTT library will accommodate these\r
+ * differences. This setting should be `false` when communicating with a\r
+ * fully-compliant MQTT broker.\r
+ *\r
+ * @attention This setting <b>MUST</b> be `true` when using the AWS IoT MQTT\r
+ * server; it <b>MUST</b> be `false` otherwise.\r
+ * @note Currently, @ref IOT_MQTT_CONNECT_INFO_INITIALIZER sets this\r
+ * this member to `true`.\r
+ */\r
+ bool awsIotMqttMode;\r
+\r
+ /**\r
+ * @brief Whether this connection is a clean session.\r
+ *\r
+ * MQTT servers can maintain and topic filter subscriptions and unacknowledged\r
+ * PUBLISH messages. These form part of an <i>MQTT session</i>, which is identified by\r
+ * the [client identifier](@ref IotMqttConnectInfo_t.pClientIdentifier).\r
+ *\r
+ * Setting this value to `true` establishes a <i>clean session</i>, which causes\r
+ * the MQTT server to discard any previous session data for a client identifier.\r
+ * When the client disconnects, the server discards all session data. If this\r
+ * value is `true`, #IotMqttConnectInfo_t.pPreviousSubscriptions and\r
+ * #IotMqttConnectInfo_t.previousSubscriptionCount are ignored.\r
+ *\r
+ * Setting this value to `false` does one of the following:\r
+ * - If no previous session exists, the MQTT server will create a new\r
+ * <i>persistent session</i>. The server may maintain subscriptions and\r
+ * unacknowledged PUBLISH messages after a client disconnects, to be restored\r
+ * once the same client identifier reconnects.\r
+ * - If a previous session exists, the MQTT server restores all of the session's\r
+ * subscriptions for the client identifier and may immediately transmit any\r
+ * unacknowledged PUBLISH packets to the client.\r
+ *\r
+ * When a client with a persistent session disconnects, the MQTT server\r
+ * continues to maintain all subscriptions and unacknowledged PUBLISH messages.\r
+ * The client must also remember the session subscriptions to restore them\r
+ * upon reconnecting. #IotMqttConnectInfo_t.pPreviousSubscriptions and\r
+ * #IotMqttConnectInfo_t.previousSubscriptionCount are used to restore a\r
+ * previous session's subscriptions client-side.\r
+ */\r
+ bool cleanSession;\r
+\r
+ /**\r
+ * @brief An array of MQTT subscriptions present in a previous session, if any.\r
+ *\r
+ * Pointer to the start of an array of subscriptions present a previous session,\r
+ * if any. These subscriptions will be immediately restored upon reconnecting.\r
+ *\r
+ * This member is ignored if it is `NULL` or #IotMqttConnectInfo_t.cleanSession\r
+ * is `true`. If this member is not `NULL`, #IotMqttConnectInfo_t.previousSubscriptionCount\r
+ * must be nonzero.\r
+ */\r
+ const IotMqttSubscription_t * pPreviousSubscriptions;\r
+\r
+ /**\r
+ * @brief The number of MQTT subscriptions present in a previous session, if any.\r
+ *\r
+ * Number of subscriptions contained in the array\r
+ * #IotMqttConnectInfo_t.pPreviousSubscriptions.\r
+ *\r
+ * This value is ignored if #IotMqttConnectInfo_t.pPreviousSubscriptions\r
+ * is `NULL` or #IotMqttConnectInfo_t.cleanSession is `true`. If\r
+ * #IotMqttConnectInfo_t.pPreviousSubscriptions is not `NULL`, this value\r
+ * must be nonzero.\r
+ */\r
+ size_t previousSubscriptionCount;\r
+\r
+ /**\r
+ * @brief A message to publish if the new MQTT connection is unexpectedly closed.\r
+ *\r
+ * A Last Will and Testament (LWT) message may be published if this connection is\r
+ * closed without sending an MQTT DISCONNECT packet. This pointer should be set to\r
+ * an #IotMqttPublishInfo_t representing any LWT message to publish. If an LWT\r
+ * is not needed, this member must be set to `NULL`.\r
+ *\r
+ * Unlike other PUBLISH messages, an LWT message is limited to 65535 bytes in\r
+ * length. Additionally, [pWillInfo->retryMs](@ref IotMqttPublishInfo_t.retryMs)\r
+ * and [pWillInfo->retryLimit](@ref IotMqttPublishInfo_t.retryLimit) will\r
+ * be ignored.\r
+ */\r
+ const IotMqttPublishInfo_t * pWillInfo;\r
+\r
+ uint16_t keepAliveSeconds; /**< @brief Period of keep-alive messages. Set to 0 to disable keep-alive. */\r
+\r
+ const char * pClientIdentifier; /**< @brief MQTT client identifier. */\r
+ uint16_t clientIdentifierLength; /**< @brief Length of #IotMqttConnectInfo_t.pClientIdentifier. */\r
+\r
+ /* These credentials are not used by AWS IoT and may be ignored if\r
+ * awsIotMqttMode is true. */\r
+ const char * pUserName; /**< @brief Username for MQTT connection. */\r
+ uint16_t userNameLength; /**< @brief Length of #IotMqttConnectInfo_t.pUserName. */\r
+ const char * pPassword; /**< @brief Password for MQTT connection. */\r
+ uint16_t passwordLength; /**< @brief Length of #IotMqttConnectInfo_t.pPassword. */\r
+} IotMqttConnectInfo_t;\r
+\r
+#if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+\r
+/**\r
+ * @cond DOXYGEN_IGNORE\r
+ * Doxygen should ignore this section.\r
+ *\r
+ * Forward declaration of the internal MQTT packet structure.\r
+ */\r
+ struct _mqttPacket;\r
+/** @endcond */\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_paramstructs\r
+ * @brief Function pointers for MQTT packet serializer overrides.\r
+ *\r
+ * These function pointers allow the MQTT serialization and deserialization functions\r
+ * to be overridden for an MQTT connection. The compile-time setting\r
+ * @ref IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES must be `1` to enable this functionality.\r
+ * See the #IotMqttSerializer_t::serialize and #IotMqttSerializer_t::deserialize\r
+ * members for a list of functions that can be overridden. In addition, the functions\r
+ * for freeing packets and determining the packet type can also be overridden. If\r
+ * @ref IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES is `1`, the serializer initialization and\r
+ * cleanup functions may be extended. See documentation of\r
+ * @ref IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES for more information.\r
+ *\r
+ * If any function pointers that are `NULL`, then the default implementation of that\r
+ * function will be used.\r
+ */\r
+ typedef struct IotMqttSerializer\r
+ {\r
+ /**\r
+ * @brief Get the MQTT packet type from a stream of bytes off the network.\r
+ *\r
+ * @param[in] pNetworkConnection Reference to the network connection.\r
+ * @param[in] pNetworkInterface Function pointers used to interact with the\r
+ * network.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_GetPacketType\r
+ */\r
+ uint8_t ( * getPacketType )( void * /* pNetworkConnection */,\r
+ const IotNetworkInterface_t * /* pNetworkInterface */ );\r
+\r
+ /**\r
+ * @brief Get the remaining length from a stream of bytes off the network.\r
+ *\r
+ * @param[in] pNetworkConnection Reference to the network connection.\r
+ * @param[in] pNetworkInterface Function pointers used to interact with the\r
+ * network.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_GetRemainingLength\r
+ */\r
+ size_t ( * getRemainingLength )( void * pNetworkConnection,\r
+ const IotNetworkInterface_t * pNetworkInterface );\r
+\r
+ /**\r
+ * @brief Free a packet generated by the serializer.\r
+ *\r
+ * This function pointer must be set if any other serializer override is set.\r
+ * @param[in] uint8_t* The packet to free.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_FreePacket\r
+ */\r
+ void ( * freePacket )( uint8_t * /* pPacket */ );\r
+\r
+ struct\r
+ {\r
+ /**\r
+ * @brief CONNECT packet serializer function.\r
+ * @param[in] IotMqttConnectInfo_t* User-provided CONNECT information.\r
+ * @param[out] uint8_t** Where the CONNECT packet is written.\r
+ * @param[out] size_t* Size of the CONNECT packet.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_SerializeConnect\r
+ */\r
+ IotMqttError_t ( * connect )( const IotMqttConnectInfo_t * /* pConnectInfo */,\r
+ uint8_t ** /* pConnectPacket */,\r
+ size_t * /* pPacketSize */ );\r
+\r
+ /**\r
+ * @brief PUBLISH packet serializer function.\r
+ * @param[in] IotMqttPublishInfo_t* User-provided PUBLISH information.\r
+ * @param[out] uint8_t** Where the PUBLISH packet is written.\r
+ * @param[out] size_t* Size of the PUBLISH packet.\r
+ * @param[out] uint16_t* The packet identifier generated for this PUBLISH.\r
+ * @param[out] uint8_t** Where the high byte of the packet identifier\r
+ * is written.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_SerializePublish\r
+ */\r
+ IotMqttError_t ( * publish )( const IotMqttPublishInfo_t * /* pPublishInfo */,\r
+ uint8_t ** /* pPublishPacket */,\r
+ size_t * /* pPacketSize */,\r
+ uint16_t * /* pPacketIdentifier */,\r
+ uint8_t ** /* pPacketIdentifierHigh */ );\r
+\r
+ /**\r
+ * @brief Set the `DUP` bit in a QoS `1` PUBLISH packet.\r
+ * @param[in] uint8_t* Pointer to the PUBLISH packet to modify.\r
+ * @param[in] uint8_t* The high byte of any packet identifier to modify.\r
+ * @param[out] uint16_t* New packet identifier (AWS IoT MQTT mode only).\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_PublishSetDup\r
+ */\r
+ void ( *publishSetDup )( uint8_t * /* pPublishPacket */,\r
+ uint8_t * /* pPacketIdentifierHigh */,\r
+ uint16_t * /* pNewPacketIdentifier */ );\r
+\r
+ /**\r
+ * @brief PUBACK packet serializer function.\r
+ * @param[in] uint16_t The packet identifier to place in PUBACK.\r
+ * @param[out] uint8_t** Where the PUBACK packet is written.\r
+ * @param[out] size_t* Size of the PUBACK packet.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_SerializePuback\r
+ */\r
+ IotMqttError_t ( * puback )( uint16_t /* packetIdentifier */,\r
+ uint8_t ** /* pPubackPacket */,\r
+ size_t * /* pPacketSize */ );\r
+\r
+ /**\r
+ * @brief SUBSCRIBE packet serializer function.\r
+ * @param[in] IotMqttSubscription_t* User-provided array of subscriptions.\r
+ * @param[in] size_t Number of elements in the subscription array.\r
+ * @param[out] uint8_t** Where the SUBSCRIBE packet is written.\r
+ * @param[out] size_t* Size of the SUBSCRIBE packet.\r
+ * @param[out] uint16_t* The packet identifier generated for this SUBSCRIBE.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_SerializeSubscribe\r
+ */\r
+ IotMqttError_t ( * subscribe )( const IotMqttSubscription_t * /* pSubscriptionList */,\r
+ size_t /* subscriptionCount */,\r
+ uint8_t ** /* pSubscribePacket */,\r
+ size_t * /* pPacketSize */,\r
+ uint16_t * /* pPacketIdentifier */ );\r
+\r
+ /**\r
+ * @brief UNSUBSCRIBE packet serializer function.\r
+ * @param[in] IotMqttSubscription_t* User-provided array of subscriptions to remove.\r
+ * @param[in] size_t Number of elements in the subscription array.\r
+ * @param[out] uint8_t** Where the UNSUBSCRIBE packet is written.\r
+ * @param[out] size_t* Size of the UNSUBSCRIBE packet.\r
+ * @param[out] uint16_t* The packet identifier generated for this UNSUBSCRIBE.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_SerializeUnsubscribe\r
+ */\r
+ IotMqttError_t ( * unsubscribe )( const IotMqttSubscription_t * /* pSubscriptionList */,\r
+ size_t /* subscriptionCount */,\r
+ uint8_t ** /* pUnsubscribePacket */,\r
+ size_t * /* pPacketSize */,\r
+ uint16_t * /* pPacketIdentifier */ );\r
+\r
+ /**\r
+ * @brief PINGREQ packet serializer function.\r
+ * @param[out] uint8_t** Where the PINGREQ packet is written.\r
+ * @param[out] size_t* Size of the PINGREQ packet.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_SerializePingreq\r
+ */\r
+ IotMqttError_t ( * pingreq )( uint8_t ** /* pPingreqPacket */,\r
+ size_t * /* pPacketSize */ );\r
+\r
+ /**\r
+ * @brief DISCONNECT packet serializer function.\r
+ * @param[out] uint8_t** Where the DISCONNECT packet is written.\r
+ * @param[out] size_t* Size of the DISCONNECT packet.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_SerializeDisconnect\r
+ */\r
+ IotMqttError_t ( * disconnect )( uint8_t ** /* pDisconnectPacket */,\r
+ size_t * /* pPacketSize */ );\r
+ } serialize; /**< @brief Overrides the packet serialization functions for a single connection. */\r
+\r
+ struct\r
+ {\r
+ /**\r
+ * @brief CONNACK packet deserializer function.\r
+ * @param[in,out] _mqttPacket* Pointer to an MQTT packet struct representing a CONNACK.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_DeserializeConnack\r
+ */\r
+ IotMqttError_t ( * connack )( struct _mqttPacket * /* pConnack */ );\r
+\r
+ /**\r
+ * @brief PUBLISH packet deserializer function.\r
+ * @param[in,out] _mqttPacket* Pointer to an MQTT packet struct representing a PUBLISH.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_DeserializePublish\r
+ */\r
+ IotMqttError_t ( * publish )( struct _mqttPacket * /* pPublish */ );\r
+\r
+ /**\r
+ * @brief PUBACK packet deserializer function.\r
+ * @param[in,out] _mqttPacket* Pointer to an MQTT packet struct representing a PUBACK.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_DeserializePuback\r
+ */\r
+ IotMqttError_t ( * puback )( struct _mqttPacket * pPuback );\r
+\r
+ /**\r
+ * @brief SUBACK packet deserializer function.\r
+ * @param[in,out] _mqttPacket* Pointer to an MQTT packet struct representing a SUBACK.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_DeserializeSuback\r
+ */\r
+ IotMqttError_t ( * suback )( struct _mqttPacket * /* pSuback */ );\r
+\r
+ /**\r
+ * @brief UNSUBACK packet deserializer function.\r
+ * @param[in,out] _mqttPacket* Pointer to an MQTT packet struct representing an UNSUBACK.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_DeserializeUnsuback\r
+ */\r
+ IotMqttError_t ( * unsuback )( struct _mqttPacket * /* pUnsuback */ );\r
+\r
+ /**\r
+ * @brief PINGRESP packet deserializer function.\r
+ * @param[in,out] _mqttPacket* Pointer to an MQTT packet struct representing a PINGRESP.\r
+ *\r
+ * <b>Default implementation:</b> #_IotMqtt_DeserializePingresp\r
+ */\r
+ IotMqttError_t ( * pingresp )( struct _mqttPacket * /* pPingresp */ );\r
+ } deserialize; /**< @brief Overrides the packet deserialization functions for a single connection. */\r
+ } IotMqttSerializer_t;\r
+#else /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+/* When MQTT packet serializer overrides are disabled, this struct is an\r
+ * incomplete type. */\r
+ typedef struct IotMqttSerializer IotMqttSerializer_t;\r
+\r
+#endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+/**\r
+ * @ingroup mqtt_datatypes_paramstructs\r
+ * @brief Infomation on the transport-layer network connection for the new MQTT\r
+ * connection.\r
+ *\r
+ * @paramfor @ref mqtt_function_connect\r
+ *\r
+ * The MQTT library needs to be able to send and receive data over a network.\r
+ * This struct provides an interface for interacting with the network.\r
+ *\r
+ * @initializer{IotMqttNetworkInfo_t,IOT_MQTT_NETWORK_INFO_INITIALIZER}\r
+ */\r
+typedef struct IotMqttNetworkInfo\r
+{\r
+ /**\r
+ * @brief Whether a new network connection should be created.\r
+ *\r
+ * When this value is `true`, a new transport-layer network connection will\r
+ * be created along with the MQTT connection. #IotMqttNetworkInfo_t::pNetworkServerInfo\r
+ * and #IotMqttNetworkInfo_t::pNetworkCredentialInfo are valid when this value\r
+ * is `true`.\r
+ *\r
+ * When this value is `false`, the MQTT connection will use a transport-layer\r
+ * network connection that has already been established. The MQTT library will\r
+ * still set the appropriate receive callback even if the network connection\r
+ * has been established.\r
+ * #IotMqttNetworkInfo_t::pNetworkConnection, which represents an established\r
+ * network connection, is valid when this value is `false`.\r
+ */\r
+ bool createNetworkConnection;\r
+\r
+ union\r
+ {\r
+ struct\r
+ {\r
+ /**\r
+ * @brief Information on the MQTT server, passed as `pConnectionInfo` to\r
+ * #IotNetworkInterface_t::create.\r
+ *\r
+ * This member is opaque to the MQTT library. It is passed to the network\r
+ * interface when creating a new network connection. It is only valid when\r
+ * #IotMqttNetworkInfo_t::createNetworkConnection is `true`.\r
+ */\r
+ void * pNetworkServerInfo;\r
+\r
+ /**\r
+ * @brief Credentials for the MQTT server, passed as `pCredentialInfo` to\r
+ * #IotNetworkInterface_t::create.\r
+ *\r
+ * This member is opaque to the MQTT library. It is passed to the network\r
+ * interface when creating a new network connection. It is only valid when\r
+ * #IotMqttNetworkInfo_t::createNetworkConnection is `true`.\r
+ */\r
+ void * pNetworkCredentialInfo;\r
+ } setup;\r
+\r
+ /**\r
+ * @brief An established transport-layer network connection.\r
+ *\r
+ * This member is opaque to the MQTT library. It is passed to the network\r
+ * interface to reference an established network connection. It is only\r
+ * valid when #IotMqttNetworkInfo_t::createNetworkConnection is `false`.\r
+ */\r
+ void * pNetworkConnection;\r
+ } u /**< @brief Valid member depends of IotMqttNetworkInfo_t.createNetworkConnection. */;\r
+\r
+ /**\r
+ * @brief The network functions used by the new MQTT connection.\r
+ *\r
+ * @attention The function pointers of the network interface must remain valid\r
+ * for the lifetime of the MQTT connection.\r
+ */\r
+ const IotNetworkInterface_t * pNetworkInterface;\r
+\r
+ /**\r
+ * @brief A callback function to invoke when this MQTT connection is disconnected.\r
+ */\r
+ IotMqttCallbackInfo_t disconnectCallback;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+\r
+ /**\r
+ * @brief MQTT packet serializer overrides used by the new MQTT connection.\r
+ *\r
+ * @attention The function pointers of the MQTT serializer overrides must\r
+ * remain valid for the lifetime of the MQTT connection.\r
+ */\r
+ const IotMqttSerializer_t * pMqttSerializer;\r
+ #endif\r
+} IotMqttNetworkInfo_t;\r
+\r
+/*------------------------- MQTT defined constants --------------------------*/\r
+\r
+/**\r
+ * @constantspage{mqtt,MQTT library}\r
+ *\r
+ * @section mqtt_constants_initializers MQTT Initializers\r
+ * @brief Provides default values for the data types of the MQTT library.\r
+ *\r
+ * @snippet this define_mqtt_initializers\r
+ *\r
+ * All user-facing data types of the MQTT library should be initialized using\r
+ * one of the following.\r
+ *\r
+ * @warning Failing to initialize an MQTT data type with the appropriate initializer\r
+ * may result in undefined behavior!\r
+ * @note The initializers may change at any time in future versions, but their\r
+ * names will remain the same.\r
+ *\r
+ * <b>Example</b>\r
+ * @code{c}\r
+ * IotMqttNetworkInfo_t networkInfo = IOT_MQTT_NETWORK_INFO_INITIALIZER;\r
+ * IotMqttSerializer_t serializer = IOT_MQTT_SERIALIZER_INITIALIZER;\r
+ * IotMqttConnectInfo_t connectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER;\r
+ * IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;\r
+ * IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;\r
+ * IotMqttCallbackInfo_t callbackInfo = IOT_MQTT_CALLBACK_INFO_INITIALIZER;\r
+ * IotMqttConnection_t connection = IOT_MQTT_CONNECTION_INITIALIZER;\r
+ * IotMqttOperation_t operation = IOT_MQTT_OPERATION_INITIALIZER;\r
+ * @endcode\r
+ *\r
+ * @section mqtt_constants_flags MQTT Function Flags\r
+ * @brief Flags that modify the behavior of MQTT library functions.\r
+ * - #IOT_MQTT_FLAG_WAITABLE <br>\r
+ * @copybrief IOT_MQTT_FLAG_WAITABLE\r
+ * - #IOT_MQTT_FLAG_CLEANUP_ONLY <br>\r
+ * @copybrief IOT_MQTT_FLAG_CLEANUP_ONLY\r
+ *\r
+ * Flags should be bitwise-ORed with each other to change the behavior of\r
+ * @ref mqtt_function_subscribe, @ref mqtt_function_unsubscribe,\r
+ * @ref mqtt_function_publish, or @ref mqtt_function_disconnect.\r
+ *\r
+ * @note The values of the flags may change at any time in future versions, but\r
+ * their names will remain the same. Additionally, flags that may be used together\r
+ * will be bitwise-exclusive of each other.\r
+ */\r
+\r
+/* @[define_mqtt_initializers] */\r
+/** @brief Initializer for #IotMqttNetworkInfo_t. */\r
+#define IOT_MQTT_NETWORK_INFO_INITIALIZER { .createNetworkConnection = true }\r
+/** @brief Initializer for #IotMqttSerializer_t. */\r
+#define IOT_MQTT_SERIALIZER_INITIALIZER { 0 }\r
+/** @brief Initializer for #IotMqttConnectInfo_t. */\r
+#define IOT_MQTT_CONNECT_INFO_INITIALIZER { .cleanSession = true }\r
+/** @brief Initializer for #IotMqttPublishInfo_t. */\r
+#define IOT_MQTT_PUBLISH_INFO_INITIALIZER { .qos = IOT_MQTT_QOS_0 }\r
+/** @brief Initializer for #IotMqttSubscription_t. */\r
+#define IOT_MQTT_SUBSCRIPTION_INITIALIZER { .qos = IOT_MQTT_QOS_0 }\r
+/** @brief Initializer for #IotMqttCallbackInfo_t. */\r
+#define IOT_MQTT_CALLBACK_INFO_INITIALIZER { 0 }\r
+/** @brief Initializer for #IotMqttConnection_t. */\r
+#define IOT_MQTT_CONNECTION_INITIALIZER NULL\r
+/** @brief Initializer for #IotMqttOperation_t. */\r
+#define IOT_MQTT_OPERATION_INITIALIZER NULL\r
+/* @[define_mqtt_initializers] */\r
+\r
+/**\r
+ * @brief Allows the use of @ref mqtt_function_wait for blocking until completion.\r
+ *\r
+ * This flag is always valid for @ref mqtt_function_subscribe and\r
+ * @ref mqtt_function_unsubscribe. If passed to @ref mqtt_function_publish,\r
+ * the parameter [pPublishInfo->qos](@ref IotMqttPublishInfo_t.qos) must not be `0`.\r
+ *\r
+ * An #IotMqttOperation_t <b>MUST</b> be provided if this flag is set. Additionally, an\r
+ * #IotMqttCallbackInfo_t <b>MUST NOT</b> be provided.\r
+ *\r
+ * @note If this flag is set, @ref mqtt_function_wait <b>MUST</b> be called to clean up\r
+ * resources.\r
+ */\r
+#define IOT_MQTT_FLAG_WAITABLE ( 0x00000001 )\r
+\r
+/**\r
+ * @brief Causes @ref mqtt_function_disconnect to only free memory and not send\r
+ * an MQTT DISCONNECT packet.\r
+ *\r
+ * This flag is only valid for @ref mqtt_function_disconnect. It should be passed\r
+ * to @ref mqtt_function_disconnect if the network goes offline or is otherwise\r
+ * unusable.\r
+ */\r
+#define IOT_MQTT_FLAG_CLEANUP_ONLY ( 0x00000001 )\r
+\r
+#endif /* ifndef IOT_MQTT_TYPES_H_ */\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file aws_mqtt_lib_ble.c\r
+ * @brief MQTT library for BLE.\r
+ */\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Standard includes. */\r
+#include <string.h>\r
+#include <stdio.h>\r
+\r
+/* FreeRTOS includes. */\r
+#include "FreeRTOS.h"\r
+\r
+#include "iot_ble_config.h"\r
+\r
+\r
+/* MQTT internal includes. */\r
+#include "platform/iot_threads.h"\r
+#include "iot_serializer.h"\r
+#include "platform/iot_network_ble.h"\r
+#include "iot_ble_data_transfer.h"\r
+#include "iot_ble_mqtt_serialize.h"\r
+#include "private/iot_mqtt_internal.h"\r
+\r
+#define _INVALID_MQTT_PACKET_TYPE ( 0xF0 )\r
+\r
+\r
+#define _IS_VALID_SERIALIZER_RET( ret, pSerializerBuf ) \\r
+ ( ( ret == IOT_SERIALIZER_SUCCESS ) || \\r
+ ( ( !pSerializerBuf ) && ( ret == IOT_SERIALIZER_BUFFER_TOO_SMALL ) ) )\r
+\r
+#define _NUM_CONNECT_PARMAS ( 4 )\r
+#define _NUM_DEFAULT_PUBLISH_PARMAS ( 4 )\r
+#define _NUM_PUBACK_PARMAS ( 2 )\r
+#define _NUM_SUBACK_PARAMS ( 4 )\r
+#define _NUM_UNSUBACK_PARAMS ( 3 )\r
+#define _NUM_DISCONNECT_PARAMS ( 1 )\r
+#define _NUM_PINGREQUEST_PARAMS ( 1 )\r
+\r
+const IotMqttSerializer_t IotBleMqttSerializer = {\r
+ .serialize.connect = IotBleMqtt_SerializeConnect,\r
+ .serialize.publish = IotBleMqtt_SerializePublish,\r
+ .serialize.publishSetDup = IotBleMqtt_PublishSetDup,\r
+ .serialize.puback = IotBleMqtt_SerializePuback,\r
+ .serialize.subscribe = IotBleMqtt_SerializeSubscribe,\r
+ .serialize.unsubscribe = IotBleMqtt_SerializeUnsubscribe,\r
+ .serialize.pingreq = IotBleMqtt_SerializePingreq,\r
+ .serialize.disconnect = IotBleMqtt_SerializeDisconnect,\r
+ .freePacket = IotBleMqtt_FreePacket,\r
+ .getPacketType = IotBleMqtt_GetPacketType,\r
+ .getRemainingLength = IotBleMqtt_GetRemainingLength,\r
+ .deserialize.connack = IotBleMqtt_DeserializeConnack,\r
+ .deserialize.publish = IotBleMqtt_DeserializePublish,\r
+ .deserialize.puback = IotBleMqtt_DeserializePuback,\r
+ .deserialize.suback = IotBleMqtt_DeserializeSuback,\r
+ .deserialize.unsuback = IotBleMqtt_DeserializeUnsuback,\r
+ .deserialize.pingresp = IotBleMqtt_DeserializePingresp\r
+};\r
+\r
+/**\r
+ * @brief Guards access to the packet identifier counter.\r
+ *\r
+ * Each packet should have a unique packet identifier. This mutex ensures that only\r
+ * one thread at a time may read the global packet identifer.\r
+ */\r
+\r
+\r
+/**\r
+ * @brief Generates a monotonically increasing identifier used in MQTT message\r
+ *\r
+ * @return Identifier for the MQTT message\r
+ */\r
+static uint16_t _nextPacketIdentifier( void );\r
+\r
+\r
+static inline uint16_t _getNumPublishParams( const IotMqttPublishInfo_t * const pPublish )\r
+{\r
+ return ( pPublish->qos > 0 ) ? ( _NUM_DEFAULT_PUBLISH_PARMAS + 1 ) : _NUM_DEFAULT_PUBLISH_PARMAS;\r
+}\r
+\r
+static IotSerializerError_t _serializeConnect( const IotMqttConnectInfo_t * const pConnectInfo,\r
+ uint8_t* const pBuffer,\r
+ size_t* const pSize );\r
+static IotSerializerError_t _serializePublish( const IotMqttPublishInfo_t * const pPublishInfo,\r
+ uint8_t * pBuffer,\r
+ size_t * pSize,\r
+ uint16_t packetIdentifier );\r
+static IotSerializerError_t _serializePubAck( uint16_t packetIdentifier,\r
+ uint8_t * pBuffer,\r
+ size_t * pSize );\r
+\r
+\r
+\r
+static IotSerializerError_t _serializeSubscribe( const IotMqttSubscription_t * const pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint8_t * const pBuffer,\r
+ size_t * const pSize,\r
+ uint16_t packetIdentifier );\r
+\r
+static IotSerializerError_t _serializeUnSubscribe( const IotMqttSubscription_t * const pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint8_t * const pBuffer,\r
+ size_t * const pSize,\r
+ uint16_t packetIdentifier );\r
+\r
+static IotSerializerError_t _serializeDisconnect( uint8_t * const pBuffer,\r
+ size_t * const pSize );\r
+\r
+\r
+static IotSerializerError_t _serializePingRequest( uint8_t * const pBuffer,\r
+ size_t * const pSize );\r
+\r
+#if LIBRARY_LOG_LEVEL > AWS_IOT_LOG_NONE\r
+\r
+/**\r
+ * @brief If logging is enabled, define a log configuration that only prints the log\r
+ * string. This is used when printing out details of deserialized MQTT packets.\r
+ */\r
+static const IotLogConfig_t _logHideAll =\r
+{\r
+ .hideLibraryName = true,\r
+ .hideLogLevel = true,\r
+ .hideTimestring = true\r
+};\r
+#endif\r
+\r
+\r
+static IotMutex_t _packetIdentifierMutex;\r
+\r
+\r
+/* Declaration of snprintf. The header stdio.h is not included because it\r
+ * includes conflicting symbols on some platforms. */\r
+extern int snprintf( char * s,\r
+ size_t n,\r
+ const char * format,\r
+ ... );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static uint16_t _nextPacketIdentifier( void )\r
+{\r
+ static uint16_t nextPacketIdentifier = 1;\r
+ uint16_t newPacketIdentifier = 0;\r
+\r
+ /* Lock the packet identifier mutex so that only one thread may read and\r
+ * modify nextPacketIdentifier. */\r
+ IotMutex_Lock( &_packetIdentifierMutex );\r
+\r
+ /* Read the next packet identifier. */\r
+ newPacketIdentifier = nextPacketIdentifier;\r
+\r
+ /* The next packet identifier will be greater by 2. This prevents packet\r
+ * identifiers from ever being 0, which is not allowed by MQTT 3.1.1. Packet\r
+ * identifiers will follow the sequence 1,3,5...65535,1,3,5... */\r
+ nextPacketIdentifier = ( uint16_t ) ( nextPacketIdentifier + ( ( uint16_t ) 2 ) );\r
+\r
+ /* Unlock the packet identifier mutex. */\r
+ IotMutex_Unlock( &_packetIdentifierMutex );\r
+\r
+ return newPacketIdentifier;\r
+}\r
+\r
+static IotSerializerError_t _serializeConnect( const IotMqttConnectInfo_t * const pConnectInfo,\r
+ uint8_t* const pBuffer,\r
+ size_t* const pSize )\r
+{\r
+ IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;\r
+ IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM ;\r
+ IotSerializerEncoderObject_t connectMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;\r
+ IotSerializerScalarData_t data = { 0 };\r
+\r
+ error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );\r
+ if( error == IOT_SERIALIZER_SUCCESS )\r
+ {\r
+\r
+ error = IOT_BLE_MESG_ENCODER.openContainer(\r
+ &encoderObj,\r
+ &connectMap,\r
+ _NUM_CONNECT_PARMAS );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_CONNECT;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &connectMap, IOT_BLE_MQTT_MSG_TYPE, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_TEXT_STRING;\r
+ data.value.u.string.pString = ( uint8_t * ) pConnectInfo->pClientIdentifier;\r
+ data.value.u.string.length = pConnectInfo->clientIdentifierLength;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &connectMap, IOT_BLE_MQTT_CLIENT_ID, data );\r
+ }\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_TEXT_STRING;\r
+ data.value.u.string.pString = ( uint8_t * ) clientcredentialMQTT_BROKER_ENDPOINT;\r
+ data.value.u.string.length = strlen( clientcredentialMQTT_BROKER_ENDPOINT );\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &connectMap, IOT_BLE_MQTT_BROKER_EP, data );\r
+ }\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_BOOL;\r
+ data.value.u.booleanValue = pConnectInfo->cleanSession;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &connectMap, IOT_BLE_MQTT_CLEAN_SESSION, data );\r
+ }\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &connectMap );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ if( pBuffer == NULL )\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );\r
+ }\r
+ else\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );\r
+ }\r
+\r
+ IOT_BLE_MESG_ENCODER.destroy( &encoderObj );\r
+ error = IOT_SERIALIZER_SUCCESS;\r
+\r
+ }\r
+\r
+ return error;\r
+}\r
+\r
+static IotSerializerError_t _serializePublish( const IotMqttPublishInfo_t * const pPublishInfo,\r
+ uint8_t * pBuffer,\r
+ size_t * pSize,\r
+ uint16_t packetIdentifier )\r
+{\r
+ IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;\r
+ IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM ;\r
+ IotSerializerEncoderObject_t publishMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;\r
+ IotSerializerScalarData_t data = { 0 };\r
+ uint16_t numPublishParams = _getNumPublishParams( pPublishInfo );\r
+\r
+ error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );\r
+\r
+ if( error == IOT_SERIALIZER_SUCCESS )\r
+ {\r
+\r
+\r
+ error = IOT_BLE_MESG_ENCODER.openContainer(\r
+ &encoderObj,\r
+ &publishMap,\r
+ numPublishParams );\r
+ }\r
+\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_PUBLISH;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &publishMap, IOT_BLE_MQTT_MSG_TYPE, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_TEXT_STRING;\r
+ data.value.u.string.pString = ( uint8_t * ) pPublishInfo->pTopicName;\r
+ data.value.u.string.length = pPublishInfo->topicNameLength;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &publishMap, IOT_BLE_MQTT_TOPIC, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = pPublishInfo->qos;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &publishMap, IOT_BLE_MQTT_QOS, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_BYTE_STRING;\r
+ data.value.u.string.pString = ( uint8_t * ) pPublishInfo->pPayload;\r
+ data.value.u.string.length = pPublishInfo->payloadLength;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &publishMap, IOT_BLE_MQTT_PAYLOAD, data );\r
+ }\r
+\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ if( pPublishInfo->qos != 0 )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = packetIdentifier;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &publishMap, IOT_BLE_MQTT_MESSAGE_ID, data );\r
+\r
+ }\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+\r
+ error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &publishMap );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ if( pBuffer == NULL )\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );\r
+\r
+ }\r
+ else\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );\r
+ }\r
+ IOT_BLE_MESG_ENCODER.destroy( &encoderObj );\r
+ error = IOT_SERIALIZER_SUCCESS;\r
+ }\r
+\r
+\r
+ return error;\r
+}\r
+\r
+static IotSerializerError_t _serializePubAck( uint16_t packetIdentifier,\r
+ uint8_t * pBuffer,\r
+ size_t * pSize )\r
+\r
+{\r
+ IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;\r
+ IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM ;\r
+ IotSerializerEncoderObject_t pubAckMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;\r
+ IotSerializerScalarData_t data = { 0 };\r
+\r
+ error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );\r
+\r
+ if( error == IOT_SERIALIZER_SUCCESS )\r
+ {\r
+\r
+ error = IOT_BLE_MESG_ENCODER.openContainer(\r
+ &encoderObj,\r
+ &pubAckMap,\r
+ _NUM_PUBACK_PARMAS );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_PUBACK;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &pubAckMap, IOT_BLE_MQTT_MSG_TYPE, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = packetIdentifier;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &pubAckMap, IOT_BLE_MQTT_MESSAGE_ID, data );\r
+ }\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &pubAckMap );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ if( pBuffer == NULL )\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );\r
+ }\r
+ else\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );\r
+ }\r
+ IOT_BLE_MESG_ENCODER.destroy( &encoderObj );\r
+ error = IOT_SERIALIZER_SUCCESS;\r
+ }\r
+\r
+ return error;\r
+}\r
+\r
+\r
+static IotSerializerError_t _serializeSubscribe( const IotMqttSubscription_t * const pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint8_t * const pBuffer,\r
+ size_t * const pSize,\r
+ uint16_t packetIdentifier )\r
+{\r
+ IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;\r
+ IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM ;\r
+ IotSerializerEncoderObject_t subscribeMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;\r
+ IotSerializerEncoderObject_t subscriptionArray = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_ARRAY;\r
+ IotSerializerScalarData_t data = { 0 };\r
+ uint16_t idx;\r
+\r
+ error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );\r
+\r
+ if( error == IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.openContainer(\r
+ &encoderObj,\r
+ &subscribeMap,\r
+ _NUM_SUBACK_PARAMS );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_SUBSCRIBE;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &subscribeMap, IOT_BLE_MQTT_MSG_TYPE, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+\r
+ error = IOT_BLE_MESG_ENCODER.openContainerWithKey(\r
+ &subscribeMap,\r
+ IOT_BLE_MQTT_TOPIC_LIST,\r
+ &subscriptionArray,\r
+ subscriptionCount );\r
+ }\r
+\r
+ for( idx = 0; idx < subscriptionCount; idx++ )\r
+ {\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_TEXT_STRING;\r
+ data.value.u.string.pString = ( uint8_t * ) pSubscriptionList[ idx ].pTopicFilter;\r
+ data.value.u.string.length = pSubscriptionList[ idx ].topicFilterLength;\r
+ error = IOT_BLE_MESG_ENCODER.append( &subscriptionArray, data );\r
+ }\r
+ else\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.closeContainer( &subscribeMap, &subscriptionArray );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+\r
+ error = IOT_BLE_MESG_ENCODER.openContainerWithKey(\r
+ &subscribeMap,\r
+ IOT_BLE_MQTT_QOS_LIST,\r
+ &subscriptionArray,\r
+ subscriptionCount );\r
+ }\r
+\r
+\r
+ for( idx = 0; idx < subscriptionCount; idx++ )\r
+ {\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = pSubscriptionList[ idx ].qos;\r
+ error = IOT_BLE_MESG_ENCODER.append( &subscriptionArray, data );\r
+ }\r
+ else\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.closeContainer( &subscribeMap, &subscriptionArray );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = packetIdentifier;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &subscribeMap, IOT_BLE_MQTT_MESSAGE_ID, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &subscribeMap );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ if( pBuffer == NULL )\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );\r
+ }\r
+ else\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );\r
+ }\r
+\r
+ IOT_BLE_MESG_ENCODER.destroy( &encoderObj );\r
+ error = IOT_SERIALIZER_SUCCESS;\r
+ }\r
+ return error;\r
+}\r
+\r
+static IotSerializerError_t _serializeUnSubscribe( const IotMqttSubscription_t * const pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint8_t * const pBuffer,\r
+ size_t * const pSize,\r
+ uint16_t packetIdentifier )\r
+{\r
+ IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;\r
+ IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM;\r
+ IotSerializerEncoderObject_t subscribeMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;\r
+ IotSerializerEncoderObject_t subscriptionArray = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_ARRAY;\r
+ IotSerializerScalarData_t data = { 0 };\r
+ uint16_t idx;\r
+\r
+ error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );\r
+\r
+ if( error == IOT_SERIALIZER_SUCCESS )\r
+ {\r
+\r
+ error = IOT_BLE_MESG_ENCODER.openContainer(\r
+ &encoderObj,\r
+ &subscribeMap,\r
+ _NUM_UNSUBACK_PARAMS );\r
+ }\r
+\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_UNSUBSCRIBE;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &subscribeMap, IOT_BLE_MQTT_MSG_TYPE, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.openContainerWithKey (\r
+ &subscribeMap,\r
+ IOT_BLE_MQTT_TOPIC_LIST,\r
+ &subscriptionArray,\r
+ subscriptionCount );\r
+ }\r
+\r
+ for( idx = 0; idx < subscriptionCount; idx++ )\r
+ {\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_TEXT_STRING;\r
+ data.value.u.string.pString = ( uint8_t * ) pSubscriptionList[ idx ].pTopicFilter;\r
+ data.value.u.string.length = pSubscriptionList[ idx ].topicFilterLength;\r
+ error = IOT_BLE_MESG_ENCODER.append( &subscriptionArray, data );\r
+ }\r
+ else\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.closeContainer( &subscribeMap, &subscriptionArray );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = packetIdentifier;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &subscribeMap, IOT_BLE_MQTT_MESSAGE_ID, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &subscribeMap );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ if( pBuffer == NULL )\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );\r
+\r
+ }\r
+ else\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );\r
+ }\r
+ IOT_BLE_MESG_ENCODER.destroy( &encoderObj );\r
+ error = IOT_SERIALIZER_SUCCESS;\r
+ }\r
+\r
+ return error;\r
+}\r
+\r
+static IotSerializerError_t _serializeDisconnect( uint8_t * const pBuffer,\r
+ size_t * const pSize )\r
+{\r
+ IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;\r
+ IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM;\r
+ IotSerializerEncoderObject_t disconnectMap = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;\r
+ IotSerializerScalarData_t data = { 0 };\r
+\r
+ error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );\r
+\r
+ if( error == IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.openContainer(\r
+ &encoderObj,\r
+ &disconnectMap,\r
+ _NUM_DISCONNECT_PARAMS );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_DISCONNECT;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &disconnectMap, IOT_BLE_MQTT_MSG_TYPE, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &disconnectMap );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ if( pBuffer == NULL )\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );\r
+ }\r
+ else\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );\r
+ }\r
+ IOT_BLE_MESG_ENCODER.destroy( &encoderObj );\r
+ error = IOT_SERIALIZER_SUCCESS;\r
+ }\r
+\r
+ return error;\r
+}\r
+\r
+static IotSerializerError_t _serializePingRequest( uint8_t * const pBuffer,\r
+ size_t * const pSize )\r
+{\r
+ IotSerializerError_t error = IOT_SERIALIZER_SUCCESS;\r
+ IotSerializerEncoderObject_t encoderObj = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_STREAM;\r
+ IotSerializerEncoderObject_t pingRequest = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP;\r
+ IotSerializerScalarData_t data = { 0 };\r
+\r
+ error = IOT_BLE_MESG_ENCODER.init( &encoderObj, pBuffer, *pSize );\r
+\r
+ if( error == IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.openContainer(\r
+ &encoderObj,\r
+ &pingRequest,\r
+ _NUM_PINGREQUEST_PARAMS );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ data.type = IOT_SERIALIZER_SCALAR_SIGNED_INT;\r
+ data.value.u.signedInt = IOT_BLE_MQTT_MSG_TYPE_PINGREQ;\r
+ error = IOT_BLE_MESG_ENCODER.appendKeyValue( &pingRequest, IOT_BLE_MQTT_MSG_TYPE, data );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ error = IOT_BLE_MESG_ENCODER.closeContainer( &encoderObj, &pingRequest );\r
+ }\r
+\r
+ if( _IS_VALID_SERIALIZER_RET( error, pBuffer ) )\r
+ {\r
+ if( pBuffer == NULL )\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getExtraBufferSizeNeeded( &encoderObj );\r
+ }\r
+ else\r
+ {\r
+ *pSize = IOT_BLE_MESG_ENCODER.getEncodedSize( &encoderObj, pBuffer );\r
+ }\r
+ IOT_BLE_MESG_ENCODER.destroy( &encoderObj );\r
+ error = IOT_SERIALIZER_SUCCESS;\r
+ }\r
+\r
+ return error;\r
+}\r
+\r
+\r
+bool IotBleMqtt_InitSerialize( void )\r
+{\r
+ /* Create the packet identifier mutex. */\r
+ return IotMutex_Create( &_packetIdentifierMutex, false );\r
+}\r
+\r
+void IotBleMqtt_CleanupSerialize( void )\r
+{\r
+ /* Destroy the packet identifier mutex */\r
+ IotMutex_Destroy( &_packetIdentifierMutex );\r
+}\r
+\r
+\r
+IotMqttError_t IotBleMqtt_SerializeConnect( const IotMqttConnectInfo_t * const pConnectInfo,\r
+ uint8_t ** const pConnectPacket,\r
+ size_t * const pPacketSize )\r
+{\r
+ uint8_t * pBuffer = NULL;\r
+ size_t bufLen = 0;\r
+ IotSerializerError_t error;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+\r
+ error = _serializeConnect( pConnectInfo, NULL, &bufLen );\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to find length of serialized CONNECT message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+\r
+ pBuffer = IotMqtt_MallocMessage( bufLen );\r
+\r
+ /* If Memory cannot be allocated log an error and return */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for CONNECT packet." );\r
+ ret = IOT_MQTT_NO_MEMORY;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ error = _serializeConnect( pConnectInfo, pBuffer, &bufLen );\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to serialize CONNECT message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ *pConnectPacket = pBuffer;\r
+ *pPacketSize = bufLen;\r
+ }\r
+ else\r
+ {\r
+ *pConnectPacket = NULL;\r
+ *pPacketSize = 0;\r
+ if( pBuffer != NULL )\r
+ {\r
+ IotMqtt_FreeMessage( pBuffer );\r
+ }\r
+ }\r
+\r
+ return ret;\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_DeserializeConnack( _mqttPacket_t * pConnack )\r
+{\r
+\r
+ IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };\r
+ IotSerializerError_t error;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+ int64_t respCode = 0L;\r
+\r
+ error = IOT_BLE_MESG_DECODER.init( &decoderObj, ( uint8_t * ) pConnack->pRemainingData, pConnack->remainingLength );\r
+ if( ( error != IOT_SERIALIZER_SUCCESS )\r
+ || ( decoderObj.type != IOT_SERIALIZER_CONTAINER_MAP ) )\r
+ {\r
+ IotLogError( "Malformed CONNACK, decoding the packet failed, decoder error = %d, type: %d", error, decoderObj.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+\r
+ error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_STATUS, &decoderValue );\r
+ if ( ( error != IOT_SERIALIZER_SUCCESS ) ||\r
+ ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )\r
+ {\r
+ IotLogError( "Invalid CONNACK, response code decode failed, error = %d, decoded value type = %d", error, decoderValue.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+ else\r
+ {\r
+\r
+ respCode = decoderValue.u.value.u.signedInt;\r
+ if( ( respCode != IOT_BLE_MQTT_STATUS_CONNECTING )\r
+ && ( respCode != IOT_BLE_MQTT_STATUS_CONNECTED ) )\r
+ {\r
+ ret = IOT_MQTT_SERVER_REFUSED;\r
+ }\r
+ }\r
+ }\r
+\r
+ IOT_BLE_MESG_DECODER.destroy( &decoderObj );\r
+\r
+ return ret;\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_SerializePublish( const IotMqttPublishInfo_t * const pPublishInfo,\r
+ uint8_t ** const pPublishPacket,\r
+ size_t * const pPacketSize,\r
+ uint16_t * const pPacketIdentifier,\r
+ uint8_t ** pPacketIdentifierHigh )\r
+{\r
+\r
+ uint8_t * pBuffer = NULL;\r
+ size_t bufLen = 0;\r
+ uint16_t usPacketIdentifier = 0;\r
+ IotSerializerError_t error;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+ (void)pPacketIdentifierHigh;\r
+\r
+ if( pPublishInfo->qos != 0 )\r
+ {\r
+ usPacketIdentifier = _nextPacketIdentifier();\r
+ }\r
+\r
+ error = _serializePublish( pPublishInfo, NULL, &bufLen, usPacketIdentifier );\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to find size of serialized PUBLISH message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+\r
+ pBuffer = IotMqtt_MallocMessage( bufLen );\r
+ /* If Memory cannot be allocated log an error and return */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for PUBLISH packet." );\r
+ ret = IOT_MQTT_NO_MEMORY;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+\r
+ error = _serializePublish( pPublishInfo, pBuffer, &bufLen, usPacketIdentifier );\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to serialize PUBLISH message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ *pPublishPacket = pBuffer;\r
+ *pPacketSize = bufLen;\r
+ *pPacketIdentifier = usPacketIdentifier;\r
+ }\r
+ else\r
+ {\r
+ if( pBuffer != NULL )\r
+ {\r
+ IotMqtt_FreeMessage( pBuffer );\r
+ }\r
+ *pPublishPacket = NULL;\r
+ *pPacketSize = 0;\r
+ }\r
+\r
+ return ret;\r
+}\r
+\r
+void IotBleMqtt_PublishSetDup( uint8_t * const pPublishPacket, uint8_t * pPacketIdentifierHigh, uint16_t * const pNewPacketIdentifier )\r
+{\r
+ /** TODO: Currently DUP flag is not supported by BLE SDKs **/\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_DeserializePublish( _mqttPacket_t * pPublish )\r
+{\r
+\r
+ IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };\r
+ IotSerializerError_t xSerializerRet;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+ xSerializerRet = IOT_BLE_MESG_DECODER.init( &decoderObj, ( uint8_t * ) pPublish->pRemainingData, pPublish->remainingLength );\r
+ if ( (xSerializerRet != IOT_SERIALIZER_SUCCESS ) ||\r
+ ( decoderObj.type != IOT_SERIALIZER_CONTAINER_MAP ) )\r
+ {\r
+\r
+ IotLogError( "Decoding PUBLISH packet failed, decoder error = %d, object type = %d", xSerializerRet, decoderObj.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ xSerializerRet = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_QOS, &decoderValue );\r
+ if ( ( xSerializerRet != IOT_SERIALIZER_SUCCESS ) ||\r
+ ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )\r
+ {\r
+ IotLogError( "QOS Value decode failed, error = %d, decoded value type = %d", xSerializerRet, decoderValue.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+ else\r
+ {\r
+ pPublish->u.pIncomingPublish->u.publish.publishInfo.qos = decoderValue.u.value.u.signedInt;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ decoderValue.u.value.u.string.pString = NULL;\r
+ decoderValue.u.value.u.string.length = 0;\r
+ xSerializerRet = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_TOPIC, &decoderValue );\r
+\r
+ if( ( xSerializerRet != IOT_SERIALIZER_SUCCESS ) ||\r
+ ( decoderValue.type != IOT_SERIALIZER_SCALAR_TEXT_STRING ) )\r
+ {\r
+ IotLogError( "Topic value decode failed, error = %d", xSerializerRet );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+ else\r
+ {\r
+ pPublish->u.pIncomingPublish->u.publish.publishInfo.pTopicName = ( const char* ) decoderValue.u.value.u.string.pString;\r
+ pPublish->u.pIncomingPublish->u.publish.publishInfo.topicNameLength = decoderValue.u.value.u.string.length;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ decoderValue.u.value.u.string.pString = NULL;\r
+ decoderValue.u.value.u.string.length = 0;\r
+ xSerializerRet = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_PAYLOAD, &decoderValue );\r
+\r
+ if( ( xSerializerRet != IOT_SERIALIZER_SUCCESS ) ||\r
+ ( decoderValue.type != IOT_SERIALIZER_SCALAR_BYTE_STRING ) )\r
+ {\r
+ IotLogError( "Payload value decode failed, error = %d", xSerializerRet );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+ else\r
+ {\r
+ pPublish->u.pIncomingPublish->u.publish.publishInfo.pPayload = ( const char* ) decoderValue.u.value.u.string.pString;\r
+ pPublish->u.pIncomingPublish->u.publish.publishInfo.payloadLength = decoderValue.u.value.u.string.length;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ if( pPublish->u.pIncomingPublish->u.publish.publishInfo.qos != 0 )\r
+ {\r
+ xSerializerRet = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_MESSAGE_ID, &decoderValue );\r
+ if ( ( xSerializerRet != IOT_SERIALIZER_SUCCESS ) ||\r
+ ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )\r
+ {\r
+ IotLogError( "Message identifier decode failed, error = %d, decoded value type = %d", xSerializerRet, decoderValue.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+ else\r
+ {\r
+ pPublish->packetIdentifier = ( uint16_t ) decoderValue.u.value.u.signedInt;\r
+ }\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ pPublish->u.pIncomingPublish->u.publish.publishInfo.retain = false;\r
+ }\r
+\r
+ IOT_BLE_MESG_DECODER.destroy( &decoderObj );\r
+\r
+ return ret;\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_SerializePuback( uint16_t packetIdentifier,\r
+ uint8_t ** const pPubackPacket,\r
+ size_t * const pPacketSize )\r
+{\r
+ uint8_t * pBuffer = NULL;\r
+ size_t bufLen = 0;\r
+ IotSerializerError_t error;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+ error = _serializePubAck( packetIdentifier, NULL, &bufLen );\r
+\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to find size of serialized PUBACK message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ pBuffer = IotMqtt_MallocMessage( bufLen );\r
+\r
+ /* If Memory cannot be allocated log an error and return */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for PUBACK packet, packet identifier = %d", packetIdentifier );\r
+ ret = IOT_MQTT_NO_MEMORY;\r
+ }\r
+ }\r
+\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ error = _serializePubAck( packetIdentifier, pBuffer, &bufLen );\r
+\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to find size of serialized PUBACK message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+ }\r
+\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ *pPubackPacket = pBuffer;\r
+ *pPacketSize = bufLen;\r
+ }\r
+ else\r
+ {\r
+ if( pBuffer != NULL )\r
+ {\r
+ IotMqtt_FreeMessage( pBuffer );\r
+ }\r
+\r
+ *pPubackPacket = NULL;\r
+ *pPacketSize = 0;\r
+ }\r
+\r
+ return ret;\r
+\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_DeserializePuback( _mqttPacket_t * pPuback )\r
+{\r
+\r
+ IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };\r
+ IotSerializerError_t error;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+ error = IOT_BLE_MESG_DECODER.init( &decoderObj, ( uint8_t * ) pPuback->pRemainingData, pPuback->remainingLength );\r
+\r
+ if ( ( error != IOT_SERIALIZER_SUCCESS )\r
+ || ( decoderObj.type != IOT_SERIALIZER_CONTAINER_MAP ) )\r
+ {\r
+ IotLogError( "Malformed PUBACK, decoding the packet failed, decoder error = %d, object type: %d", error, decoderObj.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+\r
+ error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_MESSAGE_ID, &decoderValue );\r
+\r
+ if ( ( error != IOT_SERIALIZER_SUCCESS ) ||\r
+ ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )\r
+ {\r
+ IotLogError( "Message ID decode failed, error = %d, decoded value type = %d", error, decoderValue.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+ else\r
+ {\r
+ pPuback->packetIdentifier = ( uint16_t ) decoderValue.u.value.u.signedInt;\r
+ }\r
+ }\r
+\r
+ IOT_BLE_MESG_DECODER.destroy( &decoderObj );\r
+\r
+ return ret;\r
+\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_SerializeSubscribe( const IotMqttSubscription_t * const pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint8_t ** const pSubscribePacket,\r
+ size_t * const pPacketSize,\r
+ uint16_t * const pPacketIdentifier )\r
+{\r
+ uint8_t * pBuffer = NULL;\r
+ size_t bufLen = 0;\r
+ uint16_t usPacketIdentifier = 0;\r
+ IotSerializerError_t error;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+ usPacketIdentifier = _nextPacketIdentifier();\r
+\r
+ error = _serializeSubscribe( pSubscriptionList, subscriptionCount, NULL, &bufLen, usPacketIdentifier );\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to find serialized length of SUBSCRIBE message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ pBuffer = IotMqtt_MallocMessage( bufLen );\r
+\r
+ /* If Memory cannot be allocated log an error and return */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for SUBSCRIBE message." );\r
+ ret = IOT_MQTT_NO_MEMORY;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ error = _serializeSubscribe( pSubscriptionList, subscriptionCount, pBuffer, &bufLen, usPacketIdentifier );\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to serialize SUBSCRIBE message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ *pSubscribePacket = pBuffer;\r
+ *pPacketSize = bufLen;\r
+ *pPacketIdentifier = usPacketIdentifier;\r
+ }\r
+ else\r
+ {\r
+ if( pBuffer != NULL )\r
+ {\r
+ IotMqtt_FreeMessage( pBuffer );\r
+ }\r
+\r
+ *pSubscribePacket = NULL;\r
+ *pPacketSize = 0;\r
+ }\r
+\r
+ return ret;\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_DeserializeSuback( _mqttPacket_t * pSuback )\r
+{\r
+\r
+ IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };\r
+ IotSerializerError_t error;\r
+ int64_t subscriptionStatus;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+ error = IOT_BLE_MESG_DECODER.init( &decoderObj, ( uint8_t * ) pSuback->pRemainingData, pSuback->remainingLength );\r
+\r
+ if ( ( error != IOT_SERIALIZER_SUCCESS )\r
+ || ( decoderObj.type != IOT_SERIALIZER_CONTAINER_MAP ) )\r
+ {\r
+ IotLogError( "Malformed SUBACK, decoding the packet failed, decoder error = %d, type: %d", error, decoderObj.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+\r
+ error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_MESSAGE_ID, &decoderValue );\r
+ if ( ( error != IOT_SERIALIZER_SUCCESS ) ||\r
+ ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )\r
+ {\r
+ IotLogError( "Message ID decode failed, error = %d, decoded value type = %d", error, decoderValue.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+ else\r
+ {\r
+ pSuback->packetIdentifier = ( uint16_t ) decoderValue.u.value.u.signedInt;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_STATUS, &decoderValue );\r
+ if ( ( error != IOT_SERIALIZER_SUCCESS ) ||\r
+ ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )\r
+ {\r
+ IotLogError( "Status code decode failed, error = %d, decoded value type = %d", error, decoderValue.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+ else\r
+ {\r
+ subscriptionStatus = ( uint16_t ) decoderValue.u.value.u.signedInt;\r
+ switch( subscriptionStatus )\r
+ {\r
+ case 0x00:\r
+ case 0x01:\r
+ case 0x02:\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Topic accepted, max QoS %hhu.", subscriptionStatus );\r
+ ret = IOT_MQTT_SUCCESS;\r
+ break;\r
+ case 0x80:\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Topic refused." );\r
+\r
+ /* Remove a rejected subscription from the subscription manager. */\r
+ _IotMqtt_RemoveSubscriptionByPacket(\r
+ pSuback->u.pMqttConnection,\r
+ pSuback->packetIdentifier,\r
+ 0 );\r
+ ret = IOT_MQTT_SERVER_REFUSED;\r
+ break;\r
+ default:\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Bad SUBSCRIBE status %hhu.", subscriptionStatus );\r
+\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ IOT_BLE_MESG_DECODER.destroy( &decoderObj );\r
+\r
+ return ret;\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_SerializeUnsubscribe( const IotMqttSubscription_t * const pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint8_t ** const pUnsubscribePacket,\r
+ size_t * const pPacketSize,\r
+ uint16_t * const pPacketIdentifier )\r
+{\r
+\r
+ uint8_t * pBuffer = NULL;\r
+ size_t bufLen = 0;\r
+ uint16_t usPacketIdentifier = 0;\r
+ IotSerializerError_t error;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+ usPacketIdentifier = _nextPacketIdentifier();\r
+\r
+ error = _serializeUnSubscribe( pSubscriptionList, subscriptionCount, NULL, &bufLen, usPacketIdentifier );\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to find serialized length of UNSUBSCRIBE message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ pBuffer = IotMqtt_MallocMessage( bufLen );\r
+\r
+ /* If Memory cannot be allocated log an error and return */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for UNSUBSCRIBE message." );\r
+ ret = IOT_MQTT_NO_MEMORY;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ error = _serializeUnSubscribe( pSubscriptionList, subscriptionCount, pBuffer, &bufLen, usPacketIdentifier );\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to serialize UNSUBSCRIBE message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ *pUnsubscribePacket = pBuffer;\r
+ *pPacketSize = bufLen;\r
+ *pPacketIdentifier = usPacketIdentifier;\r
+ }\r
+ else\r
+ {\r
+ if( pBuffer != NULL )\r
+ {\r
+ IotMqtt_FreeMessage( pBuffer );\r
+ }\r
+\r
+ *pUnsubscribePacket = NULL;\r
+ *pPacketSize = 0;\r
+ }\r
+\r
+ return ret;\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_DeserializeUnsuback( _mqttPacket_t * pUnsuback )\r
+{\r
+ IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };\r
+ IotSerializerError_t error;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+ error = IOT_BLE_MESG_DECODER.init( &decoderObj, ( uint8_t * ) pUnsuback->pRemainingData, pUnsuback->remainingLength );\r
+ if( ( error != IOT_SERIALIZER_SUCCESS )\r
+ || ( decoderObj.type != IOT_SERIALIZER_CONTAINER_MAP ) )\r
+ {\r
+ IotLogError( "Malformed UNSUBACK, decoding the packet failed, decoder error = %d, type:%d ", error, decoderObj.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_MESSAGE_ID, &decoderValue );\r
+ if ( ( error != IOT_SERIALIZER_SUCCESS ) ||\r
+ ( decoderValue.type != IOT_SERIALIZER_SCALAR_SIGNED_INT ) )\r
+ {\r
+ IotLogError( "UNSUBACK Message identifier decode failed, error = %d, decoded value type = %d", error, decoderValue.type );\r
+ ret = IOT_MQTT_BAD_RESPONSE;\r
+\r
+ }\r
+ else\r
+ {\r
+ pUnsuback->packetIdentifier = ( uint16_t ) decoderValue.u.value.u.signedInt;\r
+ }\r
+ }\r
+\r
+ IOT_BLE_MESG_DECODER.destroy( &decoderObj );\r
+\r
+ return IOT_MQTT_SUCCESS;\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_SerializeDisconnect( uint8_t ** const pDisconnectPacket,\r
+ size_t * const pPacketSize )\r
+{\r
+ uint8_t *pBuffer = NULL;\r
+ size_t bufLen = 0;\r
+ IotSerializerError_t error;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+ error = _serializeDisconnect( NULL, &bufLen);\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to find serialized length of DISCONNECT message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ pBuffer = IotMqtt_MallocMessage( bufLen );\r
+\r
+ /* If Memory cannot be allocated log an error and return */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for DISCONNECT message." );\r
+ ret = IOT_MQTT_NO_MEMORY;\r
+ }\r
+ }\r
+\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ error = _serializeDisconnect( pBuffer, &bufLen );\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to serialize DISCONNECT message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ *pDisconnectPacket = pBuffer;\r
+ *pPacketSize = bufLen;\r
+ }\r
+ else\r
+ {\r
+ if( pBuffer != NULL )\r
+ {\r
+ IotMqtt_FreeMessage( pBuffer );\r
+ }\r
+\r
+ *pDisconnectPacket = NULL;\r
+ *pPacketSize = 0;\r
+ }\r
+\r
+ return ret;\r
+}\r
+\r
+size_t IotBleMqtt_GetRemainingLength ( void * pNetworkConnection,\r
+ const IotNetworkInterface_t * pNetworkInterface )\r
+{\r
+ const uint8_t *pBuffer;\r
+ size_t length;\r
+\r
+ IotBleDataTransfer_PeekReceiveBuffer( *( IotBleDataTransferChannel_t ** ) ( pNetworkConnection ), &pBuffer, &length );\r
+\r
+ return length;\r
+}\r
+\r
+\r
+uint8_t IotBleMqtt_GetPacketType( void * pNetworkConnection, const IotNetworkInterface_t * pNetworkInterface )\r
+{\r
+\r
+ IotSerializerDecoderObject_t decoderObj = { 0 }, decoderValue = { 0 };\r
+ IotSerializerError_t error;\r
+ uint8_t value = 0xFF, packetType = _INVALID_MQTT_PACKET_TYPE;\r
+ const uint8_t *pBuffer;\r
+ size_t length;\r
+\r
+ IotBleDataTransfer_PeekReceiveBuffer( *( IotBleDataTransferChannel_t ** ) ( pNetworkConnection ), &pBuffer, &length );\r
+\r
+ error = IOT_BLE_MESG_DECODER.init( &decoderObj, pBuffer, length );\r
+\r
+ if( ( error == IOT_SERIALIZER_SUCCESS )\r
+ && ( decoderObj.type == IOT_SERIALIZER_CONTAINER_MAP ) )\r
+ {\r
+\r
+ error = IOT_BLE_MESG_DECODER.find( &decoderObj, IOT_BLE_MQTT_MSG_TYPE, &decoderValue );\r
+\r
+ if ( ( error == IOT_SERIALIZER_SUCCESS ) &&\r
+ ( decoderValue.type == IOT_SERIALIZER_SCALAR_SIGNED_INT ) )\r
+ {\r
+ value = ( uint16_t ) decoderValue.u.value.u.signedInt;\r
+\r
+ /** Left shift by 4 bits as MQTT library expects packet type to be upper 4 bits **/\r
+ packetType = value << 4;\r
+ }\r
+ else\r
+ {\r
+ IotLogError( "Packet type decode failed, error = %d, decoded value type = %d", error, decoderValue.type );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ IotLogError( "Decoding the packet failed, decoder error = %d, type = %d", error, decoderObj.type );\r
+ }\r
+\r
+ IOT_BLE_MESG_DECODER.destroy( &decoderObj );\r
+\r
+ return packetType;\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_SerializePingreq( uint8_t ** const pPingreqPacket,\r
+ size_t * const pPacketSize )\r
+{\r
+ uint8_t *pBuffer = NULL;\r
+ size_t bufLen = 0;\r
+ IotSerializerError_t error;\r
+ IotMqttError_t ret = IOT_MQTT_SUCCESS;\r
+\r
+ error = _serializePingRequest( NULL, &bufLen);\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to find serialized length of DISCONNECT message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ pBuffer = IotMqtt_MallocMessage( bufLen );\r
+\r
+ /* If Memory cannot be allocated log an error and return */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for DISCONNECT message." );\r
+ ret = IOT_MQTT_NO_MEMORY;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ error = _serializePingRequest( pBuffer, &bufLen );\r
+ if( error != IOT_SERIALIZER_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to serialize DISCONNECT message, error = %d", error );\r
+ ret = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if( ret == IOT_MQTT_SUCCESS )\r
+ {\r
+ *pPingreqPacket = pBuffer;\r
+ *pPacketSize = bufLen;\r
+ }\r
+ else\r
+ {\r
+ if( pBuffer != NULL )\r
+ {\r
+ IotMqtt_FreeMessage( pBuffer );\r
+ }\r
+\r
+ *pPingreqPacket = NULL;\r
+ *pPacketSize = 0;\r
+ }\r
+\r
+ return ret;\r
+\r
+}\r
+\r
+IotMqttError_t IotBleMqtt_DeserializePingresp( _mqttPacket_t * pPingresp )\r
+{\r
+ /* Ping Response for BLE contains only packet type field in CBOR, which is already decoded\r
+ in IotBleMqtt_GetPacketType() function. Returning IOT_MQTT_SUCCESS. */\r
+ return IOT_MQTT_SUCCESS;\r
+}\r
+\r
+void IotBleMqtt_FreePacket( uint8_t * pPacket )\r
+{\r
+ IotMqtt_FreeMessage( pPacket );\r
+}\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_agent.c\r
+ * @brief MQTT Agent implementation. Provides backwards compatibility between\r
+ * MQTT v2 and MQTT v1.\r
+ */\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Standard includes. */\r
+#include <string.h>\r
+\r
+/* FreeRTOS includes. */\r
+#include "FreeRTOS.h"\r
+#include "semphr.h"\r
+\r
+/* MQTT v1 includes. */\r
+#include "iot_mqtt_agent.h"\r
+#include "iot_mqtt_agent_config.h"\r
+#include "iot_mqtt_agent_config_defaults.h"\r
+\r
+/* MQTT v2 include. */\r
+#include "iot_mqtt.h"\r
+\r
+/* Platform network include. */\r
+#include "platform/iot_network_freertos.h"\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Converts FreeRTOS ticks to milliseconds.\r
+ */\r
+#define mqttTICKS_TO_MS( xTicks ) ( xTicks * 1000 / configTICK_RATE_HZ )\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Stores data to convert between the MQTT v1 subscription callback\r
+ * and the MQTT v2 subscription callback.\r
+ */\r
+#if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )\r
+ typedef struct MQTTCallback\r
+ {\r
+ BaseType_t xInUse; /**< Whether this instance is in-use. */\r
+ MQTTPublishCallback_t xFunction; /**< MQTT v1 callback function. */\r
+ void * pvParameter; /**< Parameter to xFunction. */\r
+\r
+ uint16_t usTopicFilterLength; /**< Length of pcTopicFilter. */\r
+ char pcTopicFilter[ mqttconfigSUBSCRIPTION_MANAGER_MAX_TOPIC_LENGTH ]; /**< Topic filter. */\r
+ } MQTTCallback_t;\r
+#endif\r
+\r
+/**\r
+ * @brief Stores data on an active MQTT connection.\r
+ */\r
+typedef struct MQTTConnection\r
+{\r
+ IotMqttConnection_t xMQTTConnection; /**< MQTT v2 connection handle. */\r
+ MQTTAgentCallback_t pxCallback; /**< MQTT v1 global callback. */\r
+ void * pvUserData; /**< Parameter to pxCallback. */\r
+ StaticSemaphore_t xConnectionMutex; /**< Protects from concurrent accesses. */\r
+ #if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )\r
+ MQTTCallback_t xCallbacks /**< Conversion table of MQTT v1 to MQTT v2 subscription callbacks. */\r
+ [ mqttconfigSUBSCRIPTION_MANAGER_MAX_SUBSCRIPTIONS ];\r
+ #endif\r
+} MQTTConnection_t;\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Convert an MQTT v2 return code to an MQTT v1 return code.\r
+ *\r
+ * @param[in] xMqttStatus The MQTT v2 return code.\r
+ *\r
+ * @return An equivalent MQTT v1 return code.\r
+ */\r
+static inline MQTTAgentReturnCode_t prvConvertReturnCode( IotMqttError_t xMqttStatus );\r
+\r
+/**\r
+ * @brief Wraps an MQTT v1 publish callback.\r
+ *\r
+ * @param[in] pvParameter The MQTT connection.\r
+ * @param[in] pxPublish Information about the incoming publish.\r
+ */\r
+static void prvPublishCallbackWrapper( void * pvParameter,\r
+ IotMqttCallbackParam_t * const pxPublish );\r
+\r
+/**\r
+ * @brief Wraps an MQTT v1 disconnect callback.\r
+ *\r
+ * @param[in] pvCallbackContext The MQTT connection.\r
+ * @param[in] pxDisconnect Information about the disconnect.\r
+ */\r
+static void prvDisconnectCallbackWrapper( void * pvParameter,\r
+ IotMqttCallbackParam_t * pxDisconnect );\r
+\r
+#if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )\r
+\r
+/**\r
+ * @brief Store an MQTT v1 callback in the conversion table.\r
+ *\r
+ * @param[in] pxConnection Where to store the callback.\r
+ * @param[in] pcTopicFilter Topic filter to store.\r
+ * @param[in] usTopicFilterLength Length of pcTopicFilter.\r
+ * @param[in] xCallback MQTT v1 callback to store.\r
+ * @param[in] pvParameter Parameter to xCallback.\r
+ *\r
+ * @return pdPASS if the callback was successfully stored; pdFAIL otherwise.\r
+ */\r
+ static BaseType_t prvStoreCallback( MQTTConnection_t * const pxConnection,\r
+ const char * const pcTopicFilter,\r
+ uint16_t usTopicFilterLength,\r
+ MQTTPublishCallback_t xCallback,\r
+ void * pvParameter );\r
+\r
+/**\r
+ * @brief Search the callback conversion table for the given topic filter.\r
+ *\r
+ * @param[in] pxConnection The connection containing the conversion table.\r
+ * @param[in] pcTopicFilter The topic filter to search for.\r
+ * @param[in] usTopicFilterLength The length of pcTopicFilter.\r
+ *\r
+ * @return A pointer to the callback entry if found; NULL otherwise.\r
+ * @note This function should be called with pxConnection->xConnectionMutex\r
+ * locked.\r
+ */\r
+ static MQTTCallback_t * prvFindCallback( MQTTConnection_t * const pxConnection,\r
+ const char * const pcTopicFilter,\r
+ uint16_t usTopicFilterLength );\r
+\r
+/**\r
+ * @brief Remove a topic filter from the callback conversion table.\r
+ *\r
+ * @param[in] pxConnection The connection containing the conversion table.\r
+ * @param[in] pcTopicFilter The topic filter to remove.\r
+ * @param[in] usTopicFilterLength The length of pcTopic.\r
+ */\r
+ static void prvRemoveCallback( MQTTConnection_t * const pxConnection,\r
+ const char * const pcTopicFilter,\r
+ uint16_t usTopicFilterLength );\r
+#endif /* if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 ) */\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief The number of available MQTT brokers, controlled by the constant\r
+ * mqttconfigMAX_BROKERS;\r
+ */\r
+static UBaseType_t uxAvailableBrokers = mqttconfigMAX_BROKERS;\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static inline MQTTAgentReturnCode_t prvConvertReturnCode( IotMqttError_t xMqttStatus )\r
+{\r
+ MQTTAgentReturnCode_t xStatus = eMQTTAgentSuccess;\r
+\r
+ switch( xMqttStatus )\r
+ {\r
+ case IOT_MQTT_SUCCESS:\r
+ case IOT_MQTT_STATUS_PENDING:\r
+ xStatus = eMQTTAgentSuccess;\r
+ break;\r
+\r
+ case IOT_MQTT_TIMEOUT:\r
+ xStatus = eMQTTAgentTimeout;\r
+ break;\r
+\r
+ default:\r
+ xStatus = eMQTTAgentFailure;\r
+ break;\r
+ }\r
+\r
+ return xStatus;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvPublishCallbackWrapper( void * pvParameter,\r
+ IotMqttCallbackParam_t * const pxPublish )\r
+{\r
+ BaseType_t xStatus = pdPASS;\r
+ size_t xBufferSize = 0;\r
+ uint8_t * pucMqttBuffer = NULL;\r
+ MQTTBool_t xCallbackReturn = eMQTTFalse;\r
+ MQTTConnection_t * pxConnection = ( MQTTConnection_t * ) pvParameter;\r
+ MQTTAgentCallbackParams_t xPublishData = { .xMQTTEvent = eMQTTAgentPublish };\r
+\r
+ /* Calculate the size of the MQTT buffer that must be allocated. */\r
+ if( xStatus == pdPASS )\r
+ {\r
+ xBufferSize = pxPublish->u.message.info.topicNameLength +\r
+ pxPublish->u.message.info.payloadLength;\r
+\r
+ /* Check for overflow. */\r
+ if( ( xBufferSize < pxPublish->u.message.info.topicNameLength ) ||\r
+ ( xBufferSize < pxPublish->u.message.info.payloadLength ) )\r
+ {\r
+ mqttconfigDEBUG_LOG( ( "Incoming PUBLISH message and topic name length too large.\r\n" ) );\r
+ xStatus = pdFAIL;\r
+ }\r
+ }\r
+\r
+ /* Allocate an MQTT buffer for the callback. */\r
+ if( xStatus == pdPASS )\r
+ {\r
+ pucMqttBuffer = pvPortMalloc( xBufferSize );\r
+\r
+ if( pucMqttBuffer == NULL )\r
+ {\r
+ mqttconfigDEBUG_LOG( ( "Failed to allocate memory for MQTT buffer.\r\n" ) );\r
+ xStatus = pdFAIL;\r
+ }\r
+ else\r
+ {\r
+ /* Copy the topic name and payload. The topic name and payload must be\r
+ * copied in case the user decides to take ownership of the MQTT buffer.\r
+ * The original buffer containing the MQTT topic name and payload may\r
+ * contain further unprocessed packets and must remain property of the\r
+ * MQTT library. Therefore, the topic name and payload are copied into\r
+ * another buffer for the user. */\r
+ ( void ) memcpy( pucMqttBuffer,\r
+ pxPublish->u.message.info.pTopicName,\r
+ pxPublish->u.message.info.topicNameLength );\r
+ ( void ) memcpy( pucMqttBuffer + pxPublish->u.message.info.topicNameLength,\r
+ pxPublish->u.message.info.pPayload,\r
+ pxPublish->u.message.info.payloadLength );\r
+\r
+ /* Set the members of the callback parameter. */\r
+ xPublishData.xMQTTEvent = eMQTTAgentPublish;\r
+ xPublishData.u.xPublishData.pucTopic = pucMqttBuffer;\r
+ xPublishData.u.xPublishData.usTopicLength = pxPublish->u.message.info.topicNameLength;\r
+ xPublishData.u.xPublishData.pvData = pucMqttBuffer + pxPublish->u.message.info.topicNameLength;\r
+ xPublishData.u.xPublishData.ulDataLength = ( uint32_t ) pxPublish->u.message.info.payloadLength;\r
+ xPublishData.u.xPublishData.xQos = ( MQTTQoS_t ) pxPublish->u.message.info.qos;\r
+ xPublishData.u.xPublishData.xBuffer = pucMqttBuffer;\r
+ }\r
+ }\r
+\r
+ if( xStatus == pdPASS )\r
+ {\r
+ #if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )\r
+ /* When subscription management is enabled, search for a matching subscription. */\r
+ MQTTCallback_t * pxCallbackEntry = prvFindCallback( pxConnection,\r
+ pxPublish->u.message.pTopicFilter,\r
+ pxPublish->u.message.topicFilterLength );\r
+\r
+ /* Check if a matching MQTT v1 subscription was found. */\r
+ if( pxCallbackEntry != NULL )\r
+ {\r
+ /* Invoke the topic-specific callback if it exists. */\r
+ if( pxCallbackEntry->xFunction != NULL )\r
+ {\r
+ xCallbackReturn = pxCallbackEntry->xFunction( pxCallbackEntry->pvParameter,\r
+ &( xPublishData.u.xPublishData ) );\r
+ }\r
+ else\r
+ {\r
+ /* Otherwise, invoke the global callback. */\r
+ if( pxConnection->pxCallback != NULL )\r
+ {\r
+ xCallbackReturn = ( MQTTBool_t ) pxConnection->pxCallback( pxConnection->pvUserData,\r
+ &xPublishData );\r
+ }\r
+ }\r
+ }\r
+ #else /* if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 ) */\r
+\r
+ /* When subscription management is disabled, invoke the global callback\r
+ * if one exists. */\r
+\r
+ /* When subscription management is disabled, the topic filter must be "#". */\r
+ mqttconfigASSERT( *( xPublish.message.pTopicFilter ) == '#' );\r
+ mqttconfigASSERT( xPublish.message.topicFilterLength == 1 );\r
+\r
+ if( pxConnection->pxCallback != NULL )\r
+ {\r
+ xCallbackReturn = ( MQTTBool_t ) pxConnection->pxCallback( pxConnection->pvUserData,\r
+ &xPublishData );\r
+ }\r
+ #endif /* if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 ) */\r
+ }\r
+\r
+ /* Free the MQTT buffer if the user did not take ownership of it. */\r
+ if( ( xCallbackReturn == eMQTTFalse ) && ( pucMqttBuffer != NULL ) )\r
+ {\r
+ vPortFree( pucMqttBuffer );\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvDisconnectCallbackWrapper( void * pvParameter,\r
+ IotMqttCallbackParam_t * pxDisconnect )\r
+{\r
+ MQTTConnection_t * pxConnection = ( MQTTConnection_t * ) pvParameter;\r
+ MQTTAgentCallbackParams_t xCallbackParams = { .xMQTTEvent = eMQTTAgentDisconnect };\r
+\r
+ ( void ) pxDisconnect;\r
+\r
+ /* This function should only be called if a callback was set. */\r
+ mqttconfigASSERT( pxConnection->pxCallback != NULL );\r
+\r
+ /* Invoke the MQTT v1 callback. Ignore the return value. */\r
+ pxConnection->pxCallback( pxConnection->pvUserData,\r
+ &xCallbackParams );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+#if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )\r
+ static BaseType_t prvStoreCallback( MQTTConnection_t * const pxConnection,\r
+ const char * const pcTopicFilter,\r
+ uint16_t usTopicFilterLength,\r
+ MQTTPublishCallback_t xCallback,\r
+ void * pvParameter )\r
+ {\r
+ MQTTCallback_t * pxCallback = NULL;\r
+ BaseType_t xStatus = pdFAIL, i = 0;\r
+\r
+ /* Prevent other tasks from modifying stored callbacks while this function\r
+ * runs. */\r
+ if( xSemaphoreTake( ( QueueHandle_t ) &( pxConnection->xConnectionMutex ),\r
+ portMAX_DELAY ) == pdTRUE )\r
+ {\r
+ /* Check if the topic filter already has an entry. */\r
+ pxCallback = prvFindCallback( pxConnection, pcTopicFilter, usTopicFilterLength );\r
+\r
+ if( pxCallback == NULL )\r
+ {\r
+ /* If no entry was found, find a free entry. */\r
+ for( i = 0; i < mqttconfigSUBSCRIPTION_MANAGER_MAX_SUBSCRIPTIONS; i++ )\r
+ {\r
+ if( pxConnection->xCallbacks[ i ].xInUse == pdFALSE )\r
+ {\r
+ pxConnection->xCallbacks[ i ].xInUse = pdTRUE;\r
+ pxCallback = &( pxConnection->xCallbacks[ i ] );\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ /* Set the members of the callback entry. */\r
+ if( i < mqttconfigSUBSCRIPTION_MANAGER_MAX_SUBSCRIPTIONS )\r
+ {\r
+ pxCallback->pvParameter = pvParameter;\r
+ pxCallback->usTopicFilterLength = usTopicFilterLength;\r
+ pxCallback->xFunction = xCallback;\r
+ ( void ) strncpy( pxCallback->pcTopicFilter, pcTopicFilter, usTopicFilterLength );\r
+ xStatus = pdPASS;\r
+ }\r
+\r
+ ( void ) xSemaphoreGive( ( QueueHandle_t ) &( pxConnection->xConnectionMutex ) );\r
+ }\r
+\r
+ return xStatus;\r
+ }\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+ static MQTTCallback_t * prvFindCallback( MQTTConnection_t * const pxConnection,\r
+ const char * const pcTopicFilter,\r
+ uint16_t usTopicFilterLength )\r
+ {\r
+ BaseType_t i = 0;\r
+ MQTTCallback_t * pxResult = NULL;\r
+\r
+ /* Search the callback conversion table for the topic filter. */\r
+ for( i = 0; i < mqttconfigSUBSCRIPTION_MANAGER_MAX_SUBSCRIPTIONS; i++ )\r
+ {\r
+ if( ( pxConnection->xCallbacks[ i ].usTopicFilterLength == usTopicFilterLength ) &&\r
+ ( strncmp( pxConnection->xCallbacks[ i ].pcTopicFilter,\r
+ pcTopicFilter,\r
+ usTopicFilterLength ) == 0 ) )\r
+ {\r
+ pxResult = &( pxConnection->xCallbacks[ i ] );\r
+ break;\r
+ }\r
+ }\r
+\r
+ return pxResult;\r
+ }\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+ static void prvRemoveCallback( MQTTConnection_t * const pxConnection,\r
+ const char * const pcTopicFilter,\r
+ uint16_t usTopicFilterLength )\r
+ {\r
+ MQTTCallback_t * pxCallback = NULL;\r
+\r
+ /* Prevent other tasks from modifying stored callbacks while this function\r
+ * runs. */\r
+ if( xSemaphoreTake( ( QueueHandle_t ) &( pxConnection->xConnectionMutex ),\r
+ portMAX_DELAY ) == pdTRUE )\r
+ {\r
+ /* Find the given topic filter. */\r
+ pxCallback = prvFindCallback( pxConnection, pcTopicFilter, usTopicFilterLength );\r
+\r
+ if( pxCallback != NULL )\r
+ {\r
+ /* Clear the callback entry. */\r
+ mqttconfigASSERT( pxCallback->xInUse == pdTRUE );\r
+ ( void ) memset( pxCallback, 0x00, sizeof( MQTTCallback_t ) );\r
+ }\r
+\r
+ ( void ) xSemaphoreGive( ( QueueHandle_t ) &( pxConnection->xConnectionMutex ) );\r
+ }\r
+ }\r
+#endif /* if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 ) */\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttConnection_t MQTT_AGENT_Getv2Connection( MQTTAgentHandle_t xMQTTHandle )\r
+{\r
+ MQTTConnection_t * pxConnection = ( MQTTConnection_t * ) xMQTTHandle;\r
+\r
+ return pxConnection->xMQTTConnection;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+BaseType_t MQTT_AGENT_Init( void )\r
+{\r
+ BaseType_t xStatus = pdFALSE;\r
+\r
+ /* Call the initialization function of MQTT v2. */\r
+ if( IotMqtt_Init() == IOT_MQTT_SUCCESS )\r
+ {\r
+ xStatus = pdTRUE;\r
+ }\r
+\r
+ return xStatus;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+MQTTAgentReturnCode_t MQTT_AGENT_Create( MQTTAgentHandle_t * const pxMQTTHandle )\r
+{\r
+ MQTTConnection_t * pxNewConnection = NULL;\r
+ MQTTAgentReturnCode_t xStatus = eMQTTAgentSuccess;\r
+\r
+ /* Check how many brokers are available; fail if all brokers are in use. */\r
+ taskENTER_CRITICAL();\r
+ {\r
+ if( uxAvailableBrokers == 0 )\r
+ {\r
+ xStatus = eMQTTAgentFailure;\r
+ }\r
+ else\r
+ {\r
+ uxAvailableBrokers--;\r
+ mqttconfigASSERT( uxAvailableBrokers <= mqttconfigMAX_BROKERS );\r
+ }\r
+ }\r
+ taskEXIT_CRITICAL();\r
+\r
+ /* Allocate memory for an MQTT connection. */\r
+ if( xStatus == eMQTTAgentSuccess )\r
+ {\r
+ pxNewConnection = pvPortMalloc( sizeof( MQTTConnection_t ) );\r
+\r
+ if( pxNewConnection == NULL )\r
+ {\r
+ xStatus = eMQTTAgentFailure;\r
+\r
+ taskENTER_CRITICAL();\r
+ {\r
+ uxAvailableBrokers++;\r
+ mqttconfigASSERT( uxAvailableBrokers <= mqttconfigMAX_BROKERS );\r
+ }\r
+ taskEXIT_CRITICAL();\r
+ }\r
+ else\r
+ {\r
+ ( void ) memset( pxNewConnection, 0x00, sizeof( MQTTConnection_t ) );\r
+ pxNewConnection->xMQTTConnection = IOT_MQTT_CONNECTION_INITIALIZER;\r
+ }\r
+ }\r
+\r
+ /* Create the connection mutex and set the output parameter. */\r
+ if( xStatus == eMQTTAgentSuccess )\r
+ {\r
+ ( void ) xSemaphoreCreateMutexStatic( &( pxNewConnection->xConnectionMutex ) );\r
+ *pxMQTTHandle = ( MQTTAgentHandle_t ) pxNewConnection;\r
+ }\r
+\r
+ return xStatus;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+MQTTAgentReturnCode_t MQTT_AGENT_Delete( MQTTAgentHandle_t xMQTTHandle )\r
+{\r
+ MQTTConnection_t * pxConnection = ( MQTTConnection_t * ) xMQTTHandle;\r
+\r
+ /* Clean up any allocated MQTT or network resources. */\r
+ if( pxConnection->xMQTTConnection != IOT_MQTT_CONNECTION_INITIALIZER )\r
+ {\r
+ IotMqtt_Disconnect( pxConnection->xMQTTConnection, IOT_MQTT_FLAG_CLEANUP_ONLY );\r
+ pxConnection->xMQTTConnection = IOT_MQTT_CONNECTION_INITIALIZER;\r
+ }\r
+\r
+ /* Free memory used by the MQTT connection. */\r
+ vPortFree( pxConnection );\r
+\r
+ /* Increment the number of available brokers. */\r
+ taskENTER_CRITICAL();\r
+ {\r
+ uxAvailableBrokers++;\r
+ mqttconfigASSERT( uxAvailableBrokers <= mqttconfigMAX_BROKERS );\r
+ }\r
+ taskEXIT_CRITICAL();\r
+\r
+ return eMQTTAgentSuccess;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+MQTTAgentReturnCode_t MQTT_AGENT_Connect( MQTTAgentHandle_t xMQTTHandle,\r
+ const MQTTAgentConnectParams_t * const pxConnectParams,\r
+ TickType_t xTimeoutTicks )\r
+{\r
+ MQTTAgentReturnCode_t xStatus = eMQTTAgentSuccess;\r
+ IotMqttError_t xMqttStatus = IOT_MQTT_STATUS_PENDING;\r
+ MQTTConnection_t * pxConnection = ( MQTTConnection_t * ) xMQTTHandle;\r
+ IotNetworkServerInfo_t xServerInfo = { 0 };\r
+ IotNetworkCredentials_t xCredentials = AWS_IOT_NETWORK_CREDENTIALS_AFR_INITIALIZER, * pxCredentials = NULL;\r
+ IotMqttNetworkInfo_t xNetworkInfo = IOT_MQTT_NETWORK_INFO_INITIALIZER;\r
+ IotMqttConnectInfo_t xMqttConnectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER;\r
+\r
+ /* Copy the global callback and parameter. */\r
+ pxConnection->pxCallback = pxConnectParams->pxCallback;\r
+ pxConnection->pvUserData = pxConnectParams->pvUserData;\r
+\r
+ /* Set the TLS info for a secured connection. */\r
+ if( ( pxConnectParams->xSecuredConnection == pdTRUE ) ||\r
+ ( ( pxConnectParams->xFlags & mqttagentREQUIRE_TLS ) == mqttagentREQUIRE_TLS ) )\r
+ {\r
+ pxCredentials = &xCredentials;\r
+\r
+ /* Set the server certificate. Other credentials are set by the initializer. */\r
+ xCredentials.pRootCa = pxConnectParams->pcCertificate;\r
+ xCredentials.rootCaSize = ( size_t ) pxConnectParams->ulCertificateSize;\r
+\r
+ /* Disable ALPN if requested. */\r
+ if( ( pxConnectParams->xFlags & mqttagentUSE_AWS_IOT_ALPN_443 ) == 0 )\r
+ {\r
+ xCredentials.pAlpnProtos = NULL;\r
+ }\r
+\r
+ /* Disable SNI if requested. */\r
+ if( ( pxConnectParams->xURLIsIPAddress == pdTRUE ) ||\r
+ ( ( pxConnectParams->xFlags & mqttagentURL_IS_IP_ADDRESS ) == mqttagentURL_IS_IP_ADDRESS ) )\r
+ {\r
+ xCredentials.disableSni = true;\r
+ }\r
+ }\r
+\r
+ /* Set the server info. */\r
+ xServerInfo.pHostName = pxConnectParams->pcURL;\r
+ xServerInfo.port = pxConnectParams->usPort;\r
+\r
+ /* Set the members of the network info. */\r
+ xNetworkInfo.createNetworkConnection = true;\r
+ xNetworkInfo.u.setup.pNetworkServerInfo = &xServerInfo;\r
+ xNetworkInfo.u.setup.pNetworkCredentialInfo = pxCredentials;\r
+ xNetworkInfo.pNetworkInterface = IOT_NETWORK_INTERFACE_AFR;\r
+\r
+ if( pxConnectParams->pxCallback != NULL )\r
+ {\r
+ xNetworkInfo.disconnectCallback.function = prvDisconnectCallbackWrapper;\r
+ xNetworkInfo.disconnectCallback.pCallbackContext = pxConnection;\r
+ }\r
+\r
+ /* Set the members of the MQTT connect info. */\r
+ xMqttConnectInfo.awsIotMqttMode = true;\r
+ xMqttConnectInfo.cleanSession = true;\r
+ xMqttConnectInfo.pClientIdentifier = ( const char * ) ( pxConnectParams->pucClientId );\r
+ xMqttConnectInfo.clientIdentifierLength = pxConnectParams->usClientIdLength;\r
+ xMqttConnectInfo.keepAliveSeconds = mqttconfigKEEP_ALIVE_INTERVAL_SECONDS;\r
+\r
+ /* Call MQTT v2's CONNECT function. */\r
+ xMqttStatus = IotMqtt_Connect( &xNetworkInfo,\r
+ &xMqttConnectInfo,\r
+ mqttTICKS_TO_MS( xTimeoutTicks ),\r
+ &( pxConnection->xMQTTConnection ) );\r
+ xStatus = prvConvertReturnCode( xMqttStatus );\r
+\r
+ /* Add a subscription to "#" to support the global callback when subscription\r
+ * manager is disabled. */\r
+ #if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 0 )\r
+ IotMqttSubscription_t xGlobalSubscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;\r
+ IotMqttReference_t xGlobalSubscriptionRef = IOT_MQTT_REFERENCE_INITIALIZER;\r
+\r
+ if( xStatus == eMQTTAgentSuccess )\r
+ {\r
+ xGlobalSubscription.pTopicFilter = "#";\r
+ xGlobalSubscription.topicFilterLength = 1;\r
+ xGlobalSubscription.qos = 0;\r
+ xGlobalSubscription.callback.param1 = pxConnection;\r
+ xGlobalSubscription.callback.function = prvPublishCallbackWrapper;\r
+\r
+ xMqttStatus = IotMqtt_Subscribe( pxConnection->xMQTTConnection,\r
+ &xGlobalSubscription,\r
+ 1,\r
+ IOT_MQTT_FLAG_WAITABLE,\r
+ NULL,\r
+ &xGlobalSubscriptionRef );\r
+ xStatus = prvConvertReturnCode( xMqttStatus );\r
+ }\r
+\r
+ /* Wait for the subscription to "#" to complete. */\r
+ if( xStatus == eMQTTAgentSuccess )\r
+ {\r
+ xMqttStatus = IotMqtt_Wait( xGlobalSubscriptionRef,\r
+ mqttTICKS_TO_MS( xTimeoutTicks ) );\r
+ xStatus = prvConvertReturnCode( xMqttStatus );\r
+ }\r
+ #endif /* if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 ) */\r
+\r
+ return xStatus;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+MQTTAgentReturnCode_t MQTT_AGENT_Disconnect( MQTTAgentHandle_t xMQTTHandle,\r
+ TickType_t xTimeoutTicks )\r
+{\r
+ MQTTConnection_t * pxConnection = ( MQTTConnection_t * ) xMQTTHandle;\r
+\r
+ /* MQTT v2's DISCONNECT function does not have a timeout argument. */\r
+ ( void ) xTimeoutTicks;\r
+\r
+ /* Check that the connection is established. */\r
+ if( pxConnection->xMQTTConnection != IOT_MQTT_CONNECTION_INITIALIZER )\r
+ {\r
+ /* Call MQTT v2's DISCONNECT function. */\r
+ IotMqtt_Disconnect( pxConnection->xMQTTConnection,\r
+ 0 );\r
+ pxConnection->xMQTTConnection = IOT_MQTT_CONNECTION_INITIALIZER;\r
+ }\r
+\r
+ return eMQTTAgentSuccess;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+MQTTAgentReturnCode_t MQTT_AGENT_Subscribe( MQTTAgentHandle_t xMQTTHandle,\r
+ const MQTTAgentSubscribeParams_t * const pxSubscribeParams,\r
+ TickType_t xTimeoutTicks )\r
+{\r
+ MQTTAgentReturnCode_t xStatus = eMQTTAgentSuccess;\r
+ IotMqttError_t xMqttStatus = IOT_MQTT_STATUS_PENDING;\r
+ MQTTConnection_t * pxConnection = ( MQTTConnection_t * ) xMQTTHandle;\r
+ IotMqttSubscription_t xSubscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;\r
+\r
+ /* Store the topic filter if subscription management is enabled. */\r
+ #if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )\r
+ /* Check topic filter length. */\r
+ if( pxSubscribeParams->usTopicLength > mqttconfigSUBSCRIPTION_MANAGER_MAX_TOPIC_LENGTH )\r
+ {\r
+ xStatus = eMQTTAgentFailure;\r
+ }\r
+\r
+ /* Store the subscription. */\r
+ if( prvStoreCallback( pxConnection,\r
+ ( const char * ) pxSubscribeParams->pucTopic,\r
+ pxSubscribeParams->usTopicLength,\r
+ pxSubscribeParams->pxPublishCallback,\r
+ pxSubscribeParams->pvPublishCallbackContext ) == pdFAIL )\r
+ {\r
+ xStatus = eMQTTAgentFailure;\r
+ }\r
+ #endif /* if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 ) */\r
+\r
+ /* Call MQTT v2 blocking SUBSCRIBE function. */\r
+ if( xStatus == eMQTTAgentSuccess )\r
+ {\r
+ /* Set the members of the MQTT subscription. */\r
+ xSubscription.pTopicFilter = ( const char * ) ( pxSubscribeParams->pucTopic );\r
+ xSubscription.topicFilterLength = pxSubscribeParams->usTopicLength;\r
+ xSubscription.qos = ( IotMqttQos_t ) pxSubscribeParams->xQoS;\r
+ xSubscription.callback.pCallbackContext = pxConnection;\r
+ xSubscription.callback.function = prvPublishCallbackWrapper;\r
+\r
+ xMqttStatus = IotMqtt_TimedSubscribe( pxConnection->xMQTTConnection,\r
+ &xSubscription,\r
+ 1,\r
+ 0,\r
+ mqttTICKS_TO_MS( xTimeoutTicks ) );\r
+ xStatus = prvConvertReturnCode( xMqttStatus );\r
+ }\r
+\r
+ return xStatus;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+MQTTAgentReturnCode_t MQTT_AGENT_Unsubscribe( MQTTAgentHandle_t xMQTTHandle,\r
+ const MQTTAgentUnsubscribeParams_t * const pxUnsubscribeParams,\r
+ TickType_t xTimeoutTicks )\r
+{\r
+ IotMqttError_t xMqttStatus = IOT_MQTT_STATUS_PENDING;\r
+ MQTTConnection_t * pxConnection = ( MQTTConnection_t * ) xMQTTHandle;\r
+ IotMqttSubscription_t xSubscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;\r
+\r
+ /* Remove any subscription callback that may be registered. */\r
+ #if ( mqttconfigENABLE_SUBSCRIPTION_MANAGEMENT == 1 )\r
+ prvRemoveCallback( pxConnection,\r
+ ( const char * ) ( pxUnsubscribeParams->pucTopic ),\r
+ pxUnsubscribeParams->usTopicLength );\r
+ #endif\r
+\r
+ /* Set the members of the subscription to remove. */\r
+ xSubscription.pTopicFilter = ( const char * ) ( pxUnsubscribeParams->pucTopic );\r
+ xSubscription.topicFilterLength = pxUnsubscribeParams->usTopicLength;\r
+ xSubscription.callback.pCallbackContext = pxConnection;\r
+ xSubscription.callback.function = prvPublishCallbackWrapper;\r
+\r
+ /* Call MQTT v2 blocking UNSUBSCRIBE function. */\r
+ xMqttStatus = IotMqtt_TimedUnsubscribe( pxConnection->xMQTTConnection,\r
+ &xSubscription,\r
+ 1,\r
+ 0,\r
+ mqttTICKS_TO_MS( xTimeoutTicks ) );\r
+\r
+ return prvConvertReturnCode( xMqttStatus );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+MQTTAgentReturnCode_t MQTT_AGENT_Publish( MQTTAgentHandle_t xMQTTHandle,\r
+ const MQTTAgentPublishParams_t * const pxPublishParams,\r
+ TickType_t xTimeoutTicks )\r
+{\r
+ IotMqttError_t xMqttStatus = IOT_MQTT_STATUS_PENDING;\r
+ MQTTConnection_t * pxConnection = ( MQTTConnection_t * ) xMQTTHandle;\r
+ IotMqttPublishInfo_t xPublishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;\r
+\r
+ /* Set the members of the publish info. */\r
+ xPublishInfo.pTopicName = ( const char * ) pxPublishParams->pucTopic;\r
+ xPublishInfo.topicNameLength = pxPublishParams->usTopicLength;\r
+ xPublishInfo.qos = ( IotMqttQos_t ) pxPublishParams->xQoS;\r
+ xPublishInfo.pPayload = ( const void * ) pxPublishParams->pvData;\r
+ xPublishInfo.payloadLength = pxPublishParams->ulDataLength;\r
+\r
+ /* Call the MQTT v2 blocking PUBLISH function. */\r
+ xMqttStatus = IotMqtt_TimedPublish( pxConnection->xMQTTConnection,\r
+ &xPublishInfo,\r
+ 0,\r
+ mqttTICKS_TO_MS( xTimeoutTicks ) );\r
+\r
+ return prvConvertReturnCode( xMqttStatus );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+MQTTAgentReturnCode_t MQTT_AGENT_ReturnBuffer( MQTTAgentHandle_t xMQTTHandle,\r
+ MQTTBufferHandle_t xBufferHandle )\r
+{\r
+ ( void ) xMQTTHandle;\r
+\r
+ /* Free the MQTT buffer. */\r
+ vPortFree( xBufferHandle );\r
+\r
+ return eMQTTAgentSuccess;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_api.c\r
+ * @brief Implements most user-facing functions of the MQTT library.\r
+ */\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Standard includes. */\r
+#include <string.h>\r
+\r
+/* Error handling include. */\r
+#include "private/iot_error.h"\r
+\r
+/* MQTT internal include. */\r
+#include "private/iot_mqtt_internal.h"\r
+\r
+/* Platform layer includes. */\r
+#include "platform/iot_clock.h"\r
+#include "platform/iot_threads.h"\r
+\r
+/* Validate MQTT configuration settings. */\r
+#if IOT_MQTT_ENABLE_ASSERTS != 0 && IOT_MQTT_ENABLE_ASSERTS != 1\r
+ #error "IOT_MQTT_ENABLE_ASSERTS must be 0 or 1."\r
+#endif\r
+#if IOT_MQTT_ENABLE_METRICS != 0 && IOT_MQTT_ENABLE_METRICS != 1\r
+ #error "IOT_MQTT_ENABLE_METRICS must be 0 or 1."\r
+#endif\r
+#if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES != 0 && IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES != 1\r
+ #error "IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES must be 0 or 1."\r
+#endif\r
+#if IOT_MQTT_RESPONSE_WAIT_MS <= 0\r
+ #error "IOT_MQTT_RESPONSE_WAIT_MS cannot be 0 or negative."\r
+#endif\r
+#if IOT_MQTT_RETRY_MS_CEILING <= 0\r
+ #error "IOT_MQTT_RETRY_MS_CEILING cannot be 0 or negative."\r
+#endif\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Set the unsubscribed flag of an MQTT subscription.\r
+ *\r
+ * @param[in] pSubscriptionLink Pointer to the link member of an #_mqttSubscription_t.\r
+ * @param[in] pMatch Not used.\r
+ *\r
+ * @return Always returns `true`.\r
+ */\r
+static bool _mqttSubscription_setUnsubscribe( const IotLink_t * pSubscriptionLink,\r
+ void * pMatch );\r
+\r
+/**\r
+ * @brief Destroy an MQTT subscription if its reference count is 0.\r
+ *\r
+ * @param[in] pData The subscription to destroy. This parameter is of type\r
+ * `void*` for compatibility with [free]\r
+ * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).\r
+ */\r
+static void _mqttSubscription_tryDestroy( void * pData );\r
+\r
+/**\r
+ * @brief Decrement the reference count of an MQTT operation and attempt to\r
+ * destroy it.\r
+ *\r
+ * @param[in] pData The operation data to destroy. This parameter is of type\r
+ * `void*` for compatibility with [free]\r
+ * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).\r
+ */\r
+static void _mqttOperation_tryDestroy( void * pData );\r
+\r
+/**\r
+ * @brief Create a keep-alive job for an MQTT connection.\r
+ *\r
+ * @param[in] pNetworkInfo User-provided network information for the new\r
+ * connection.\r
+ * @param[in] keepAliveSeconds User-provided keep-alive interval.\r
+ * @param[out] pMqttConnection The MQTT connection associated with the keep-alive.\r
+ *\r
+ * @return `true` if the keep-alive job was successfully created; `false` otherwise.\r
+ */\r
+static bool _createKeepAliveJob( const IotMqttNetworkInfo_t * pNetworkInfo,\r
+ uint16_t keepAliveSeconds,\r
+ _mqttConnection_t * pMqttConnection );\r
+\r
+/**\r
+ * @brief Creates a new MQTT connection and initializes its members.\r
+ *\r
+ * @param[in] awsIotMqttMode Specifies if this connection is to an AWS IoT MQTT server.\r
+ * @param[in] pNetworkInfo User-provided network information for the new\r
+ * connection.\r
+ * @param[in] keepAliveSeconds User-provided keep-alive interval for the new connection.\r
+ *\r
+ * @return Pointer to a newly-created MQTT connection; `NULL` on failure.\r
+ */\r
+static _mqttConnection_t * _createMqttConnection( bool awsIotMqttMode,\r
+ const IotMqttNetworkInfo_t * pNetworkInfo,\r
+ uint16_t keepAliveSeconds );\r
+\r
+/**\r
+ * @brief Destroys the members of an MQTT connection.\r
+ *\r
+ * @param[in] pMqttConnection Which connection to destroy.\r
+ */\r
+static void _destroyMqttConnection( _mqttConnection_t * pMqttConnection );\r
+\r
+/**\r
+ * @brief The common component of both @ref mqtt_function_subscribe and @ref\r
+ * mqtt_function_unsubscribe.\r
+ *\r
+ * See @ref mqtt_function_subscribe or @ref mqtt_function_unsubscribe for a\r
+ * description of the parameters and return values.\r
+ */\r
+static IotMqttError_t _subscriptionCommon( IotMqttOperationType_t operation,\r
+ IotMqttConnection_t mqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint32_t flags,\r
+ const IotMqttCallbackInfo_t * pCallbackInfo,\r
+ IotMqttOperation_t * pOperationReference );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _mqttSubscription_setUnsubscribe( const IotLink_t * pSubscriptionLink,\r
+ void * pMatch )\r
+{\r
+ /* Because this function is called from a container function, the given link\r
+ * must never be NULL. */\r
+ IotMqtt_Assert( pSubscriptionLink != NULL );\r
+\r
+ _mqttSubscription_t * pSubscription = IotLink_Container( _mqttSubscription_t,\r
+ pSubscriptionLink,\r
+ link );\r
+\r
+ /* Silence warnings about unused parameters. */\r
+ ( void ) pMatch;\r
+\r
+ /* Set the unsubscribed flag. */\r
+ pSubscription->unsubscribed = true;\r
+\r
+ return true;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void _mqttSubscription_tryDestroy( void * pData )\r
+{\r
+ _mqttSubscription_t * pSubscription = ( _mqttSubscription_t * ) pData;\r
+\r
+ /* Reference count must not be negative. */\r
+ IotMqtt_Assert( pSubscription->references >= 0 );\r
+\r
+ /* Unsubscribed flag should be set. */\r
+ IotMqtt_Assert( pSubscription->unsubscribed == true );\r
+\r
+ /* Free the subscription if it has no references. */\r
+ if( pSubscription->references == 0 )\r
+ {\r
+ IotMqtt_FreeSubscription( pSubscription );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void _mqttOperation_tryDestroy( void * pData )\r
+{\r
+ _mqttOperation_t * pOperation = ( _mqttOperation_t * ) pData;\r
+ IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;\r
+\r
+ /* Incoming PUBLISH operations may always be freed. */\r
+ if( pOperation->incomingPublish == true )\r
+ {\r
+ /* Cancel the incoming PUBLISH operation's job. */\r
+ taskPoolStatus = IotTaskPool_TryCancel( IOT_SYSTEM_TASKPOOL,\r
+ pOperation->job,\r
+ NULL );\r
+\r
+ /* If the operation's job was not canceled, it must be already executing.\r
+ * Any other return value is invalid. */\r
+ IotMqtt_Assert( ( taskPoolStatus == IOT_TASKPOOL_SUCCESS ) ||\r
+ ( taskPoolStatus == IOT_TASKPOOL_CANCEL_FAILED ) );\r
+\r
+ /* Check if the incoming PUBLISH job was canceled. */\r
+ if( taskPoolStatus == IOT_TASKPOOL_SUCCESS )\r
+ {\r
+ /* Job was canceled. Process incoming PUBLISH now to clean up. */\r
+ _IotMqtt_ProcessIncomingPublish( IOT_SYSTEM_TASKPOOL,\r
+ pOperation->job,\r
+ pOperation );\r
+ }\r
+ else\r
+ {\r
+ /* The executing job will process the PUBLISH, so nothing is done here. */\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* Decrement reference count and destroy operation if possible. */\r
+ if( _IotMqtt_DecrementOperationReferences( pOperation, true ) == true )\r
+ {\r
+ _IotMqtt_DestroyOperation( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _createKeepAliveJob( const IotMqttNetworkInfo_t * pNetworkInfo,\r
+ uint16_t keepAliveSeconds,\r
+ _mqttConnection_t * pMqttConnection )\r
+{\r
+ bool status = true;\r
+ IotMqttError_t serializeStatus = IOT_MQTT_SUCCESS;\r
+ IotTaskPoolError_t jobStatus = IOT_TASKPOOL_SUCCESS;\r
+\r
+ /* Network information is not used when MQTT packet serializers are disabled. */\r
+ ( void ) pNetworkInfo;\r
+\r
+ /* Default PINGREQ serializer function. */\r
+ IotMqttError_t ( * serializePingreq )( uint8_t **,\r
+ size_t * ) = _IotMqtt_SerializePingreq;\r
+\r
+ /* Convert the keep-alive interval to milliseconds. */\r
+ pMqttConnection->keepAliveMs = keepAliveSeconds * 1000;\r
+ pMqttConnection->nextKeepAliveMs = pMqttConnection->keepAliveMs;\r
+\r
+ /* Choose a PINGREQ serializer function. */\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pNetworkInfo->pMqttSerializer != NULL )\r
+ {\r
+ if( pNetworkInfo->pMqttSerializer->serialize.pingreq != NULL )\r
+ {\r
+ serializePingreq = pNetworkInfo->pMqttSerializer->serialize.pingreq;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Generate a PINGREQ packet. */\r
+ serializeStatus = serializePingreq( &( pMqttConnection->pPingreqPacket ),\r
+ &( pMqttConnection->pingreqPacketSize ) );\r
+\r
+ if( serializeStatus != IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to allocate PINGREQ packet for new connection." );\r
+\r
+ status = false;\r
+ }\r
+ else\r
+ {\r
+ /* Create the task pool job that processes keep-alive. */\r
+ jobStatus = IotTaskPool_CreateJob( _IotMqtt_ProcessKeepAlive,\r
+ pMqttConnection,\r
+ &( pMqttConnection->keepAliveJobStorage ),\r
+ &( pMqttConnection->keepAliveJob ) );\r
+\r
+ /* Task pool job creation for a pre-allocated job should never fail.\r
+ * Abort the program if it does. */\r
+ if( jobStatus != IOT_TASKPOOL_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to create keep-alive job for new connection." );\r
+\r
+ IotMqtt_Assert( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Keep-alive references its MQTT connection, so increment reference. */\r
+ ( pMqttConnection->references )++;\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static _mqttConnection_t * _createMqttConnection( bool awsIotMqttMode,\r
+ const IotMqttNetworkInfo_t * pNetworkInfo,\r
+ uint16_t keepAliveSeconds )\r
+{\r
+ IOT_FUNCTION_ENTRY( bool, true );\r
+ _mqttConnection_t * pMqttConnection = NULL;\r
+ bool referencesMutexCreated = false, subscriptionMutexCreated = false;\r
+\r
+ /* Allocate memory for the new MQTT connection. */\r
+ pMqttConnection = IotMqtt_MallocConnection( sizeof( _mqttConnection_t ) );\r
+\r
+ if( pMqttConnection == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for new connection." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ /* Clear the MQTT connection, then copy the MQTT server mode, network\r
+ * interface, and disconnect callback. */\r
+ ( void ) memset( pMqttConnection, 0x00, sizeof( _mqttConnection_t ) );\r
+ pMqttConnection->awsIotMqttMode = awsIotMqttMode;\r
+ pMqttConnection->pNetworkInterface = pNetworkInfo->pNetworkInterface;\r
+ pMqttConnection->disconnectCallback = pNetworkInfo->disconnectCallback;\r
+\r
+ /* Start a new MQTT connection with a reference count of 1. */\r
+ pMqttConnection->references = 1;\r
+ }\r
+\r
+ /* Create the references mutex for a new connection. It is a recursive mutex. */\r
+ referencesMutexCreated = IotMutex_Create( &( pMqttConnection->referencesMutex ), true );\r
+\r
+ if( referencesMutexCreated == false )\r
+ {\r
+ IotLogError( "Failed to create references mutex for new connection." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Create the subscription mutex for a new connection. */\r
+ subscriptionMutexCreated = IotMutex_Create( &( pMqttConnection->subscriptionMutex ), false );\r
+\r
+ if( subscriptionMutexCreated == false )\r
+ {\r
+ IotLogError( "Failed to create subscription mutex for new connection." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Create the new connection's subscription and operation lists. */\r
+ IotListDouble_Create( &( pMqttConnection->subscriptionList ) );\r
+ IotListDouble_Create( &( pMqttConnection->pendingProcessing ) );\r
+ IotListDouble_Create( &( pMqttConnection->pendingResponse ) );\r
+\r
+ /* AWS IoT service limits set minimum and maximum values for keep-alive interval.\r
+ * Adjust the user-provided keep-alive interval based on these requirements. */\r
+ if( awsIotMqttMode == true )\r
+ {\r
+ if( keepAliveSeconds < AWS_IOT_MQTT_SERVER_MIN_KEEPALIVE )\r
+ {\r
+ keepAliveSeconds = AWS_IOT_MQTT_SERVER_MIN_KEEPALIVE;\r
+ }\r
+ else if( keepAliveSeconds > AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE )\r
+ {\r
+ keepAliveSeconds = AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE;\r
+ }\r
+ else if( keepAliveSeconds == 0 )\r
+ {\r
+ keepAliveSeconds = AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check if keep-alive is active for this connection. */\r
+ if( keepAliveSeconds != 0 )\r
+ {\r
+ if( _createKeepAliveJob( pNetworkInfo,\r
+ keepAliveSeconds,\r
+ pMqttConnection ) == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Clean up mutexes and connection if this function failed. */\r
+ IOT_FUNCTION_CLEANUP_BEGIN();\r
+\r
+ if( status == false )\r
+ {\r
+ if( subscriptionMutexCreated == true )\r
+ {\r
+ IotMutex_Destroy( &( pMqttConnection->subscriptionMutex ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( referencesMutexCreated == true )\r
+ {\r
+ IotMutex_Destroy( &( pMqttConnection->referencesMutex ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pMqttConnection != NULL )\r
+ {\r
+ IotMqtt_FreeConnection( pMqttConnection );\r
+ pMqttConnection = NULL;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return pMqttConnection;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void _destroyMqttConnection( _mqttConnection_t * pMqttConnection )\r
+{\r
+ IotNetworkError_t networkStatus = IOT_NETWORK_SUCCESS;\r
+\r
+ /* Clean up keep-alive if still allocated. */\r
+ if( pMqttConnection->keepAliveMs != 0 )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) Cleaning up keep-alive.", pMqttConnection );\r
+\r
+ _IotMqtt_FreePacket( pMqttConnection->pPingreqPacket );\r
+\r
+ /* Clear data about the keep-alive. */\r
+ pMqttConnection->keepAliveMs = 0;\r
+ pMqttConnection->pPingreqPacket = NULL;\r
+ pMqttConnection->pingreqPacketSize = 0;\r
+\r
+ /* Decrement reference count. */\r
+ pMqttConnection->references--;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* A connection to be destroyed should have no keep-alive and at most 1\r
+ * reference. */\r
+ IotMqtt_Assert( pMqttConnection->references <= 1 );\r
+ IotMqtt_Assert( pMqttConnection->keepAliveMs == 0 );\r
+ IotMqtt_Assert( pMqttConnection->pPingreqPacket == NULL );\r
+ IotMqtt_Assert( pMqttConnection->pingreqPacketSize == 0 );\r
+\r
+ /* Remove all subscriptions. */\r
+ IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );\r
+ IotListDouble_RemoveAllMatches( &( pMqttConnection->subscriptionList ),\r
+ _mqttSubscription_setUnsubscribe,\r
+ NULL,\r
+ _mqttSubscription_tryDestroy,\r
+ offsetof( _mqttSubscription_t, link ) );\r
+ IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );\r
+\r
+ /* Destroy an owned network connection. */\r
+ if( pMqttConnection->ownNetworkConnection == true )\r
+ {\r
+ networkStatus = pMqttConnection->pNetworkInterface->destroy( pMqttConnection->pNetworkConnection );\r
+\r
+ if( networkStatus != IOT_NETWORK_SUCCESS )\r
+ {\r
+ IotLogWarn( "(MQTT connection %p) Failed to destroy network connection.",\r
+ pMqttConnection );\r
+ }\r
+ else\r
+ {\r
+ IotLogInfo( "(MQTT connection %p) Network connection destroyed.",\r
+ pMqttConnection );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Destroy mutexes. */\r
+ IotMutex_Destroy( &( pMqttConnection->referencesMutex ) );\r
+ IotMutex_Destroy( &( pMqttConnection->subscriptionMutex ) );\r
+\r
+ IotLogDebug( "(MQTT connection %p) Connection destroyed.", pMqttConnection );\r
+\r
+ /* Free connection. */\r
+ IotMqtt_FreeConnection( pMqttConnection );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static IotMqttError_t _subscriptionCommon( IotMqttOperationType_t operation,\r
+ IotMqttConnection_t mqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint32_t flags,\r
+ const IotMqttCallbackInfo_t * pCallbackInfo,\r
+ IotMqttOperation_t * pOperationReference )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ _mqttOperation_t * pSubscriptionOperation = NULL;\r
+\r
+ /* Subscription serializer function. */\r
+ IotMqttError_t ( * serializeSubscription )( const IotMqttSubscription_t *,\r
+ size_t,\r
+ uint8_t **,\r
+ size_t *,\r
+ uint16_t * ) = NULL;\r
+\r
+ /* This function should only be called for subscribe or unsubscribe. */\r
+ IotMqtt_Assert( ( operation == IOT_MQTT_SUBSCRIBE ) ||\r
+ ( operation == IOT_MQTT_UNSUBSCRIBE ) );\r
+\r
+ /* Check that all elements in the subscription list are valid. */\r
+ if( _IotMqtt_ValidateSubscriptionList( operation,\r
+ mqttConnection->awsIotMqttMode,\r
+ pSubscriptionList,\r
+ subscriptionCount ) == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check that a reference pointer is provided for a waitable operation. */\r
+ if( ( flags & IOT_MQTT_FLAG_WAITABLE ) == IOT_MQTT_FLAG_WAITABLE )\r
+ {\r
+ if( pOperationReference == NULL )\r
+ {\r
+ IotLogError( "Reference must be provided for a waitable %s.",\r
+ IotMqtt_OperationType( operation ) );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Choose a subscription serialize function. */\r
+ if( operation == IOT_MQTT_SUBSCRIBE )\r
+ {\r
+ serializeSubscription = _IotMqtt_SerializeSubscribe;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( mqttConnection->pSerializer != NULL )\r
+ {\r
+ if( mqttConnection->pSerializer->serialize.subscribe != NULL )\r
+ {\r
+ serializeSubscription = mqttConnection->pSerializer->serialize.subscribe;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+ }\r
+ else\r
+ {\r
+ serializeSubscription = _IotMqtt_SerializeUnsubscribe;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( mqttConnection->pSerializer != NULL )\r
+ {\r
+ if( mqttConnection->pSerializer->serialize.unsubscribe != NULL )\r
+ {\r
+ serializeSubscription = mqttConnection->pSerializer->serialize.unsubscribe;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+ }\r
+\r
+ /* Remove the MQTT subscription list for an UNSUBSCRIBE. */\r
+ if( operation == IOT_MQTT_UNSUBSCRIBE )\r
+ {\r
+ _IotMqtt_RemoveSubscriptionByTopicFilter( mqttConnection,\r
+ pSubscriptionList,\r
+ subscriptionCount );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Create a subscription operation. */\r
+ status = _IotMqtt_CreateOperation( mqttConnection,\r
+ flags,\r
+ pCallbackInfo,\r
+ &pSubscriptionOperation );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Check the subscription operation data and set the operation type. */\r
+ IotMqtt_Assert( pSubscriptionOperation->u.operation.status == IOT_MQTT_STATUS_PENDING );\r
+ IotMqtt_Assert( pSubscriptionOperation->u.operation.retry.limit == 0 );\r
+ pSubscriptionOperation->u.operation.type = operation;\r
+\r
+ /* Generate a subscription packet from the subscription list. */\r
+ status = serializeSubscription( pSubscriptionList,\r
+ subscriptionCount,\r
+ &( pSubscriptionOperation->u.operation.pMqttPacket ),\r
+ &( pSubscriptionOperation->u.operation.packetSize ),\r
+ &( pSubscriptionOperation->u.operation.packetIdentifier ) );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Check the serialized MQTT packet. */\r
+ IotMqtt_Assert( pSubscriptionOperation->u.operation.pMqttPacket != NULL );\r
+ IotMqtt_Assert( pSubscriptionOperation->u.operation.packetSize > 0 );\r
+\r
+ /* Add the subscription list for a SUBSCRIBE. */\r
+ if( operation == IOT_MQTT_SUBSCRIBE )\r
+ {\r
+ status = _IotMqtt_AddSubscriptions( mqttConnection,\r
+ pSubscriptionOperation->u.operation.packetIdentifier,\r
+ pSubscriptionList,\r
+ subscriptionCount );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+ }\r
+\r
+ /* Set the reference, if provided. */\r
+ if( pOperationReference != NULL )\r
+ {\r
+ *pOperationReference = pSubscriptionOperation;\r
+ }\r
+\r
+ /* Schedule the subscription operation for network transmission. */\r
+ status = _IotMqtt_ScheduleOperation( pSubscriptionOperation,\r
+ _IotMqtt_ProcessSend,\r
+ 0 );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogError( "(MQTT connection %p) Failed to schedule %s for sending.",\r
+ mqttConnection,\r
+ IotMqtt_OperationType( operation ) );\r
+\r
+ if( operation == IOT_MQTT_SUBSCRIBE )\r
+ {\r
+ _IotMqtt_RemoveSubscriptionByPacket( mqttConnection,\r
+ pSubscriptionOperation->u.operation.packetIdentifier,\r
+ -1 );\r
+ }\r
+\r
+ /* Clear the previously set (and now invalid) reference. */\r
+ if( pOperationReference != NULL )\r
+ {\r
+ *pOperationReference = IOT_MQTT_OPERATION_INITIALIZER;\r
+ }\r
+\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+\r
+ /* Clean up if this function failed. */\r
+ IOT_FUNCTION_CLEANUP_BEGIN();\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ if( pSubscriptionOperation != NULL )\r
+ {\r
+ _IotMqtt_DestroyOperation( pSubscriptionOperation );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ status = IOT_MQTT_STATUS_PENDING;\r
+\r
+ IotLogInfo( "(MQTT connection %p) %s operation scheduled.",\r
+ mqttConnection,\r
+ IotMqtt_OperationType( operation ) );\r
+ }\r
+\r
+ IOT_FUNCTION_CLEANUP_END();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+bool _IotMqtt_IncrementConnectionReferences( _mqttConnection_t * pMqttConnection )\r
+{\r
+ bool disconnected = false;\r
+\r
+ /* Lock the mutex protecting the reference count. */\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* Reference count must not be negative. */\r
+ IotMqtt_Assert( pMqttConnection->references >= 0 );\r
+\r
+ /* Read connection status. */\r
+ disconnected = pMqttConnection->disconnected;\r
+\r
+ /* Increment the connection's reference count if it is not disconnected. */\r
+ if( disconnected == false )\r
+ {\r
+ ( pMqttConnection->references )++;\r
+ IotLogDebug( "(MQTT connection %p) Reference count changed from %ld to %ld.",\r
+ pMqttConnection,\r
+ ( long int ) pMqttConnection->references - 1,\r
+ ( long int ) pMqttConnection->references );\r
+ }\r
+ else\r
+ {\r
+ IotLogWarn( "(MQTT connection %p) Attempt to use closed connection.", pMqttConnection );\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ return( disconnected == false );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_DecrementConnectionReferences( _mqttConnection_t * pMqttConnection )\r
+{\r
+ bool destroyConnection = false;\r
+\r
+ /* Lock the mutex protecting the reference count. */\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* Decrement reference count. It must not be negative. */\r
+ ( pMqttConnection->references )--;\r
+ IotMqtt_Assert( pMqttConnection->references >= 0 );\r
+\r
+ IotLogDebug( "(MQTT connection %p) Reference count changed from %ld to %ld.",\r
+ pMqttConnection,\r
+ ( long int ) pMqttConnection->references + 1,\r
+ ( long int ) pMqttConnection->references );\r
+\r
+ /* Check if this connection may be destroyed. */\r
+ if( pMqttConnection->references == 0 )\r
+ {\r
+ destroyConnection = true;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* Destroy an unreferenced MQTT connection. */\r
+ if( destroyConnection == true )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) Connection will be destroyed now.",\r
+ pMqttConnection );\r
+ _destroyMqttConnection( pMqttConnection );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t IotMqtt_Init( void )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_SUCCESS;\r
+\r
+ /* Call any additional serializer initialization function if serializer\r
+ * overrides are enabled. */\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ #ifdef _IotMqtt_InitSerializeAdditional\r
+ if( _IotMqtt_InitSerializeAdditional() == false )\r
+ {\r
+ status = IOT_MQTT_INIT_FAILED;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Log initialization status. */\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to initialize MQTT library serializer. " );\r
+ }\r
+ else\r
+ {\r
+ IotLogInfo( "MQTT library successfully initialized." );\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void IotMqtt_Cleanup( void )\r
+{\r
+ /* Call any additional serializer cleanup initialization function if serializer\r
+ * overrides are enabled. */\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ #ifdef _IotMqtt_CleanupSerializeAdditional\r
+ _IotMqtt_CleanupSerializeAdditional();\r
+ #endif\r
+ #endif\r
+\r
+ IotLogInfo( "MQTT library cleanup done." );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t IotMqtt_Connect( const IotMqttNetworkInfo_t * pNetworkInfo,\r
+ const IotMqttConnectInfo_t * pConnectInfo,\r
+ uint32_t timeoutMs,\r
+ IotMqttConnection_t * const pMqttConnection )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ bool networkCreated = false, ownNetworkConnection = false;\r
+ IotNetworkError_t networkStatus = IOT_NETWORK_SUCCESS;\r
+ IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;\r
+ void * pNetworkConnection = NULL;\r
+ _mqttOperation_t * pOperation = NULL;\r
+ _mqttConnection_t * pNewMqttConnection = NULL;\r
+\r
+ /* Default CONNECT serializer function. */\r
+ IotMqttError_t ( * serializeConnect )( const IotMqttConnectInfo_t *,\r
+ uint8_t **,\r
+ size_t * ) = _IotMqtt_SerializeConnect;\r
+\r
+ /* Network info must not be NULL. */\r
+ if( pNetworkInfo == NULL )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Validate network interface and connect info. */\r
+ if( _IotMqtt_ValidateConnect( pConnectInfo ) == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* If will info is provided, check that it is valid. */\r
+ if( pConnectInfo->pWillInfo != NULL )\r
+ {\r
+ if( _IotMqtt_ValidatePublish( pConnectInfo->awsIotMqttMode,\r
+ pConnectInfo->pWillInfo ) == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else if( pConnectInfo->pWillInfo->payloadLength > UINT16_MAX )\r
+ {\r
+ /* Will message payloads cannot be larger than 65535. This restriction\r
+ * applies only to will messages, and not normal PUBLISH messages. */\r
+ IotLogError( "Will payload cannot be larger than 65535." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* If previous subscriptions are provided, check that they are valid. */\r
+ if( pConnectInfo->cleanSession == false )\r
+ {\r
+ if( pConnectInfo->pPreviousSubscriptions != NULL )\r
+ {\r
+ if( _IotMqtt_ValidateSubscriptionList( IOT_MQTT_SUBSCRIBE,\r
+ pConnectInfo->awsIotMqttMode,\r
+ pConnectInfo->pPreviousSubscriptions,\r
+ pConnectInfo->previousSubscriptionCount ) == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Create a new MQTT connection if requested. Otherwise, copy the existing\r
+ * network connection. */\r
+ if( pNetworkInfo->createNetworkConnection == true )\r
+ {\r
+ networkStatus = pNetworkInfo->pNetworkInterface->create( pNetworkInfo->u.setup.pNetworkServerInfo,\r
+ pNetworkInfo->u.setup.pNetworkCredentialInfo,\r
+ &pNetworkConnection );\r
+\r
+ if( networkStatus == IOT_NETWORK_SUCCESS )\r
+ {\r
+ networkCreated = true;\r
+\r
+ /* This MQTT connection owns the network connection it created and\r
+ * should destroy it on cleanup. */\r
+ ownNetworkConnection = true;\r
+ }\r
+ else\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NETWORK_ERROR );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ pNetworkConnection = pNetworkInfo->u.pNetworkConnection;\r
+ networkCreated = true;\r
+ }\r
+\r
+ IotLogInfo( "Establishing new MQTT connection." );\r
+\r
+ /* Initialize a new MQTT connection object. */\r
+ pNewMqttConnection = _createMqttConnection( pConnectInfo->awsIotMqttMode,\r
+ pNetworkInfo,\r
+ pConnectInfo->keepAliveSeconds );\r
+\r
+ if( pNewMqttConnection == NULL )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );\r
+ }\r
+ else\r
+ {\r
+ /* Set the network connection associated with the MQTT connection. */\r
+ pNewMqttConnection->pNetworkConnection = pNetworkConnection;\r
+ pNewMqttConnection->ownNetworkConnection = ownNetworkConnection;\r
+\r
+ /* Set the MQTT packet serializer overrides. */\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ pNewMqttConnection->pSerializer = pNetworkInfo->pMqttSerializer;\r
+ #endif\r
+ }\r
+\r
+ /* Set the MQTT receive callback. */\r
+ networkStatus = pNewMqttConnection->pNetworkInterface->setReceiveCallback( pNetworkConnection,\r
+ IotMqtt_ReceiveCallback,\r
+ pNewMqttConnection );\r
+\r
+ if( networkStatus != IOT_NETWORK_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to set MQTT network receive callback." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NETWORK_ERROR );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Create a CONNECT operation. */\r
+ status = _IotMqtt_CreateOperation( pNewMqttConnection,\r
+ IOT_MQTT_FLAG_WAITABLE,\r
+ NULL,\r
+ &pOperation );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Ensure the members set by operation creation and serialization\r
+ * are appropriate for a blocking CONNECT. */\r
+ IotMqtt_Assert( pOperation->u.operation.status == IOT_MQTT_STATUS_PENDING );\r
+ IotMqtt_Assert( ( pOperation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE )\r
+ == IOT_MQTT_FLAG_WAITABLE );\r
+ IotMqtt_Assert( pOperation->u.operation.retry.limit == 0 );\r
+\r
+ /* Set the operation type. */\r
+ pOperation->u.operation.type = IOT_MQTT_CONNECT;\r
+\r
+ /* Add previous session subscriptions. */\r
+ if( pConnectInfo->pPreviousSubscriptions != NULL )\r
+ {\r
+ /* Previous subscription count should have been validated as nonzero. */\r
+ IotMqtt_Assert( pConnectInfo->previousSubscriptionCount > 0 );\r
+\r
+ status = _IotMqtt_AddSubscriptions( pNewMqttConnection,\r
+ 2,\r
+ pConnectInfo->pPreviousSubscriptions,\r
+ pConnectInfo->previousSubscriptionCount );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Choose a CONNECT serializer function. */\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pNewMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pNewMqttConnection->pSerializer->serialize.connect != NULL )\r
+ {\r
+ serializeConnect = pNewMqttConnection->pSerializer->serialize.connect;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Convert the connect info and will info objects to an MQTT CONNECT packet. */\r
+ status = serializeConnect( pConnectInfo,\r
+ &( pOperation->u.operation.pMqttPacket ),\r
+ &( pOperation->u.operation.packetSize ) );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check the serialized MQTT packet. */\r
+ IotMqtt_Assert( pOperation->u.operation.pMqttPacket != NULL );\r
+ IotMqtt_Assert( pOperation->u.operation.packetSize > 0 );\r
+\r
+ /* Add the CONNECT operation to the send queue for network transmission. */\r
+ status = _IotMqtt_ScheduleOperation( pOperation,\r
+ _IotMqtt_ProcessSend,\r
+ 0 );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to enqueue CONNECT for sending." );\r
+ }\r
+ else\r
+ {\r
+ /* Wait for the CONNECT operation to complete, i.e. wait for CONNACK. */\r
+ status = IotMqtt_Wait( pOperation,\r
+ timeoutMs );\r
+\r
+ /* The call to wait cleans up the CONNECT operation, so set the pointer\r
+ * to NULL. */\r
+ pOperation = NULL;\r
+ }\r
+\r
+ /* When a connection is successfully established, schedule keep-alive job. */\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ /* Check if a keep-alive job should be scheduled. */\r
+ if( pNewMqttConnection->keepAliveMs != 0 )\r
+ {\r
+ IotLogDebug( "Scheduling first MQTT keep-alive job." );\r
+\r
+ taskPoolStatus = IotTaskPool_ScheduleDeferred( IOT_SYSTEM_TASKPOOL,\r
+ pNewMqttConnection->keepAliveJob,\r
+ pNewMqttConnection->nextKeepAliveMs );\r
+\r
+ if( taskPoolStatus != IOT_TASKPOOL_SUCCESS )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_SCHEDULING_ERROR );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_CLEANUP_BEGIN();\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogError( "Failed to establish new MQTT connection, error %s.",\r
+ IotMqtt_strerror( status ) );\r
+\r
+ /* The network connection must be closed if it was created. */\r
+ if( networkCreated == true )\r
+ {\r
+ networkStatus = pNetworkInfo->pNetworkInterface->close( pNetworkConnection );\r
+\r
+ if( networkStatus != IOT_NETWORK_SUCCESS )\r
+ {\r
+ IotLogWarn( "Failed to close network connection." );\r
+ }\r
+ else\r
+ {\r
+ IotLogInfo( "Network connection closed on error." );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pOperation != NULL )\r
+ {\r
+ _IotMqtt_DestroyOperation( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pNewMqttConnection != NULL )\r
+ {\r
+ _destroyMqttConnection( pNewMqttConnection );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ IotLogInfo( "New MQTT connection %p established.", pMqttConnection );\r
+\r
+ /* Set the output parameter. */\r
+ *pMqttConnection = pNewMqttConnection;\r
+ }\r
+\r
+ IOT_FUNCTION_CLEANUP_END();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void IotMqtt_Disconnect( IotMqttConnection_t mqttConnection,\r
+ uint32_t flags )\r
+{\r
+ bool disconnected = false;\r
+ IotMqttError_t status = IOT_MQTT_STATUS_PENDING;\r
+ _mqttOperation_t * pOperation = NULL;\r
+\r
+ IotLogInfo( "(MQTT connection %p) Disconnecting connection.", mqttConnection );\r
+\r
+ /* Read the connection status. */\r
+ IotMutex_Lock( &( mqttConnection->referencesMutex ) );\r
+ disconnected = mqttConnection->disconnected;\r
+ IotMutex_Unlock( &( mqttConnection->referencesMutex ) );\r
+\r
+ /* Only send a DISCONNECT packet if the connection is active and the "cleanup only"\r
+ * flag is not set. */\r
+ if( disconnected == false )\r
+ {\r
+ if( ( flags & IOT_MQTT_FLAG_CLEANUP_ONLY ) == 0 )\r
+ {\r
+ /* Create a DISCONNECT operation. This function blocks until the DISCONNECT\r
+ * packet is sent, so it sets IOT_MQTT_FLAG_WAITABLE. */\r
+ status = _IotMqtt_CreateOperation( mqttConnection,\r
+ IOT_MQTT_FLAG_WAITABLE,\r
+ NULL,\r
+ &pOperation );\r
+\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ /* Ensure that the members set by operation creation and serialization\r
+ * are appropriate for a blocking DISCONNECT. */\r
+ IotMqtt_Assert( pOperation->u.operation.status == IOT_MQTT_STATUS_PENDING );\r
+ IotMqtt_Assert( ( pOperation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE )\r
+ == IOT_MQTT_FLAG_WAITABLE );\r
+ IotMqtt_Assert( pOperation->u.operation.retry.limit == 0 );\r
+\r
+ /* Set the operation type. */\r
+ pOperation->u.operation.type = IOT_MQTT_DISCONNECT;\r
+\r
+ /* Choose a disconnect serializer. */\r
+ IotMqttError_t ( * serializeDisconnect )( uint8_t **,\r
+ size_t * ) = _IotMqtt_SerializeDisconnect;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( mqttConnection->pSerializer != NULL )\r
+ {\r
+ if( mqttConnection->pSerializer->serialize.disconnect != NULL )\r
+ {\r
+ serializeDisconnect = mqttConnection->pSerializer->serialize.disconnect;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Generate a DISCONNECT packet. */\r
+ status = serializeDisconnect( &( pOperation->u.operation.pMqttPacket ),\r
+ &( pOperation->u.operation.packetSize ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ /* Check the serialized MQTT packet. */\r
+ IotMqtt_Assert( pOperation->u.operation.pMqttPacket != NULL );\r
+ IotMqtt_Assert( pOperation->u.operation.packetSize > 0 );\r
+\r
+ /* Schedule the DISCONNECT operation for network transmission. */\r
+ if( _IotMqtt_ScheduleOperation( pOperation,\r
+ _IotMqtt_ProcessSend,\r
+ 0 ) != IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogWarn( "(MQTT connection %p) Failed to schedule DISCONNECT for sending.",\r
+ mqttConnection );\r
+ _IotMqtt_DestroyOperation( pOperation );\r
+ }\r
+ else\r
+ {\r
+ /* Wait a short time for the DISCONNECT packet to be transmitted. */\r
+ status = IotMqtt_Wait( pOperation,\r
+ IOT_MQTT_RESPONSE_WAIT_MS );\r
+\r
+ /* A wait on DISCONNECT should only ever return SUCCESS, TIMEOUT,\r
+ * or NETWORK ERROR. */\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogInfo( "(MQTT connection %p) Connection disconnected.", mqttConnection );\r
+ }\r
+ else\r
+ {\r
+ IotMqtt_Assert( ( status == IOT_MQTT_TIMEOUT ) ||\r
+ ( status == IOT_MQTT_NETWORK_ERROR ) );\r
+\r
+ IotLogWarn( "(MQTT connection %p) DISCONNECT not sent, error %s.",\r
+ mqttConnection,\r
+ IotMqtt_strerror( status ) );\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Close the underlying network connection. This also cleans up keep-alive. */\r
+ _IotMqtt_CloseNetworkConnection( IOT_MQTT_DISCONNECT_CALLED,\r
+ mqttConnection );\r
+\r
+ /* Check if the connection may be destroyed. */\r
+ IotMutex_Lock( &( mqttConnection->referencesMutex ) );\r
+\r
+ /* At this point, the connection should be marked disconnected. */\r
+ IotMqtt_Assert( mqttConnection->disconnected == true );\r
+\r
+ /* Attempt cancel and destroy each operation in the connection's lists. */\r
+ IotListDouble_RemoveAll( &( mqttConnection->pendingProcessing ),\r
+ _mqttOperation_tryDestroy,\r
+ offsetof( _mqttOperation_t, link ) );\r
+\r
+ IotListDouble_RemoveAll( &( mqttConnection->pendingResponse ),\r
+ _mqttOperation_tryDestroy,\r
+ offsetof( _mqttOperation_t, link ) );\r
+\r
+ IotMutex_Unlock( &( mqttConnection->referencesMutex ) );\r
+\r
+ /* Decrement the connection reference count and destroy it if possible. */\r
+ _IotMqtt_DecrementConnectionReferences( mqttConnection );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t IotMqtt_Subscribe( IotMqttConnection_t mqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint32_t flags,\r
+ const IotMqttCallbackInfo_t * pCallbackInfo,\r
+ IotMqttOperation_t * pSubscribeOperation )\r
+{\r
+ return _subscriptionCommon( IOT_MQTT_SUBSCRIBE,\r
+ mqttConnection,\r
+ pSubscriptionList,\r
+ subscriptionCount,\r
+ flags,\r
+ pCallbackInfo,\r
+ pSubscribeOperation );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t IotMqtt_TimedSubscribe( IotMqttConnection_t mqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint32_t flags,\r
+ uint32_t timeoutMs )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_STATUS_PENDING;\r
+ IotMqttOperation_t subscribeOperation = IOT_MQTT_OPERATION_INITIALIZER;\r
+\r
+ /* Flags are not used, but the parameter is present for future compatibility. */\r
+ ( void ) flags;\r
+\r
+ /* Call the asynchronous SUBSCRIBE function. */\r
+ status = IotMqtt_Subscribe( mqttConnection,\r
+ pSubscriptionList,\r
+ subscriptionCount,\r
+ IOT_MQTT_FLAG_WAITABLE,\r
+ NULL,\r
+ &subscribeOperation );\r
+\r
+ /* Wait for the SUBSCRIBE operation to complete. */\r
+ if( status == IOT_MQTT_STATUS_PENDING )\r
+ {\r
+ status = IotMqtt_Wait( subscribeOperation, timeoutMs );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Ensure that a status was set. */\r
+ IotMqtt_Assert( status != IOT_MQTT_STATUS_PENDING );\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t IotMqtt_Unsubscribe( IotMqttConnection_t mqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint32_t flags,\r
+ const IotMqttCallbackInfo_t * pCallbackInfo,\r
+ IotMqttOperation_t * pUnsubscribeOperation )\r
+{\r
+ return _subscriptionCommon( IOT_MQTT_UNSUBSCRIBE,\r
+ mqttConnection,\r
+ pSubscriptionList,\r
+ subscriptionCount,\r
+ flags,\r
+ pCallbackInfo,\r
+ pUnsubscribeOperation );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t IotMqtt_TimedUnsubscribe( IotMqttConnection_t mqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint32_t flags,\r
+ uint32_t timeoutMs )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_STATUS_PENDING;\r
+ IotMqttOperation_t unsubscribeOperation = IOT_MQTT_OPERATION_INITIALIZER;\r
+\r
+ /* Flags are not used, but the parameter is present for future compatibility. */\r
+ ( void ) flags;\r
+\r
+ /* Call the asynchronous UNSUBSCRIBE function. */\r
+ status = IotMqtt_Unsubscribe( mqttConnection,\r
+ pSubscriptionList,\r
+ subscriptionCount,\r
+ IOT_MQTT_FLAG_WAITABLE,\r
+ NULL,\r
+ &unsubscribeOperation );\r
+\r
+ /* Wait for the UNSUBSCRIBE operation to complete. */\r
+ if( status == IOT_MQTT_STATUS_PENDING )\r
+ {\r
+ status = IotMqtt_Wait( unsubscribeOperation, timeoutMs );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Ensure that a status was set. */\r
+ IotMqtt_Assert( status != IOT_MQTT_STATUS_PENDING );\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t IotMqtt_Publish( IotMqttConnection_t mqttConnection,\r
+ const IotMqttPublishInfo_t * pPublishInfo,\r
+ uint32_t flags,\r
+ const IotMqttCallbackInfo_t * pCallbackInfo,\r
+ IotMqttOperation_t * pPublishOperation )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ _mqttOperation_t * pOperation = NULL;\r
+ uint8_t ** pPacketIdentifierHigh = NULL;\r
+\r
+ /* Default PUBLISH serializer function. */\r
+ IotMqttError_t ( * serializePublish )( const IotMqttPublishInfo_t *,\r
+ uint8_t **,\r
+ size_t *,\r
+ uint16_t *,\r
+ uint8_t ** ) = _IotMqtt_SerializePublish;\r
+\r
+ /* Check that the PUBLISH information is valid. */\r
+ if( _IotMqtt_ValidatePublish( mqttConnection->awsIotMqttMode,\r
+ pPublishInfo ) == false )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check that no notification is requested for a QoS 0 publish. */\r
+ if( pPublishInfo->qos == IOT_MQTT_QOS_0 )\r
+ {\r
+ if( pCallbackInfo != NULL )\r
+ {\r
+ IotLogError( "QoS 0 PUBLISH should not have notification parameters set." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else if( ( flags & IOT_MQTT_FLAG_WAITABLE ) != 0 )\r
+ {\r
+ IotLogError( "QoS 0 PUBLISH should not have notification parameters set." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pPublishOperation != NULL )\r
+ {\r
+ IotLogWarn( "Ignoring reference parameter for QoS 0 publish." );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check that a reference pointer is provided for a waitable operation. */\r
+ if( ( flags & IOT_MQTT_FLAG_WAITABLE ) == IOT_MQTT_FLAG_WAITABLE )\r
+ {\r
+ if( pPublishOperation == NULL )\r
+ {\r
+ IotLogError( "Reference must be provided for a waitable PUBLISH." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Create a PUBLISH operation. */\r
+ status = _IotMqtt_CreateOperation( mqttConnection,\r
+ flags,\r
+ pCallbackInfo,\r
+ &pOperation );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check the PUBLISH operation data and set the operation type. */\r
+ IotMqtt_Assert( pOperation->u.operation.status == IOT_MQTT_STATUS_PENDING );\r
+ pOperation->u.operation.type = IOT_MQTT_PUBLISH_TO_SERVER;\r
+\r
+ /* Choose a PUBLISH serializer function. */\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( mqttConnection->pSerializer != NULL )\r
+ {\r
+ if( mqttConnection->pSerializer->serialize.publish != NULL )\r
+ {\r
+ serializePublish = mqttConnection->pSerializer->serialize.publish;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* In AWS IoT MQTT mode, a pointer to the packet identifier must be saved. */\r
+ if( mqttConnection->awsIotMqttMode == true )\r
+ {\r
+ pPacketIdentifierHigh = &( pOperation->u.operation.pPacketIdentifierHigh );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Generate a PUBLISH packet from pPublishInfo. */\r
+ status = serializePublish( pPublishInfo,\r
+ &( pOperation->u.operation.pMqttPacket ),\r
+ &( pOperation->u.operation.packetSize ),\r
+ &( pOperation->u.operation.packetIdentifier ),\r
+ pPacketIdentifierHigh );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check the serialized MQTT packet. */\r
+ IotMqtt_Assert( pOperation->u.operation.pMqttPacket != NULL );\r
+ IotMqtt_Assert( pOperation->u.operation.packetSize > 0 );\r
+\r
+ /* Initialize PUBLISH retry if retryLimit is set. */\r
+ if( pPublishInfo->retryLimit > 0 )\r
+ {\r
+ /* A QoS 0 PUBLISH may not be retried. */\r
+ if( pPublishInfo->qos != IOT_MQTT_QOS_0 )\r
+ {\r
+ pOperation->u.operation.retry.limit = pPublishInfo->retryLimit;\r
+ pOperation->u.operation.retry.nextPeriod = pPublishInfo->retryMs;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Set the reference, if provided. */\r
+ if( pPublishInfo->qos != IOT_MQTT_QOS_0 )\r
+ {\r
+ if( pPublishOperation != NULL )\r
+ {\r
+ *pPublishOperation = pOperation;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Add the PUBLISH operation to the send queue for network transmission. */\r
+ status = _IotMqtt_ScheduleOperation( pOperation,\r
+ _IotMqtt_ProcessSend,\r
+ 0 );\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogError( "(MQTT connection %p) Failed to enqueue PUBLISH for sending.",\r
+ mqttConnection );\r
+\r
+ /* Clear the previously set (and now invalid) reference. */\r
+ if( pPublishInfo->qos != IOT_MQTT_QOS_0 )\r
+ {\r
+ if( pPublishOperation != NULL )\r
+ {\r
+ *pPublishOperation = IOT_MQTT_OPERATION_INITIALIZER;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Clean up the PUBLISH operation if this function fails. Otherwise, set the\r
+ * appropriate return code based on QoS. */\r
+ IOT_FUNCTION_CLEANUP_BEGIN();\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ if( pOperation != NULL )\r
+ {\r
+ _IotMqtt_DestroyOperation( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if( pPublishInfo->qos > IOT_MQTT_QOS_0 )\r
+ {\r
+ status = IOT_MQTT_STATUS_PENDING;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotLogInfo( "(MQTT connection %p) MQTT PUBLISH operation queued.",\r
+ mqttConnection );\r
+ }\r
+\r
+ IOT_FUNCTION_CLEANUP_END();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t IotMqtt_TimedPublish( IotMqttConnection_t mqttConnection,\r
+ const IotMqttPublishInfo_t * pPublishInfo,\r
+ uint32_t flags,\r
+ uint32_t timeoutMs )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_STATUS_PENDING;\r
+ IotMqttOperation_t publishOperation = IOT_MQTT_OPERATION_INITIALIZER,\r
+ * pPublishOperation = NULL;\r
+\r
+ /* Clear the flags. */\r
+ flags = 0;\r
+\r
+ /* Set the waitable flag and reference for QoS 1 PUBLISH. */\r
+ if( pPublishInfo->qos == IOT_MQTT_QOS_1 )\r
+ {\r
+ flags = IOT_MQTT_FLAG_WAITABLE;\r
+ pPublishOperation = &publishOperation;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Call the asynchronous PUBLISH function. */\r
+ status = IotMqtt_Publish( mqttConnection,\r
+ pPublishInfo,\r
+ flags,\r
+ NULL,\r
+ pPublishOperation );\r
+\r
+ /* Wait for a queued QoS 1 PUBLISH to complete. */\r
+ if( pPublishInfo->qos == IOT_MQTT_QOS_1 )\r
+ {\r
+ if( status == IOT_MQTT_STATUS_PENDING )\r
+ {\r
+ status = IotMqtt_Wait( publishOperation, timeoutMs );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t IotMqtt_Wait( IotMqttOperation_t operation,\r
+ uint32_t timeoutMs )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_SUCCESS;\r
+ _mqttConnection_t * pMqttConnection = operation->pMqttConnection;\r
+\r
+ /* Validate the given operation reference. */\r
+ if( _IotMqtt_ValidateOperation( operation ) == false )\r
+ {\r
+ status = IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check the MQTT connection status. */\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ if( pMqttConnection->disconnected == true )\r
+ {\r
+ IotLogError( "(MQTT connection %p, %s operation %p) MQTT connection is closed. "\r
+ "Operation cannot be waited on.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( operation->u.operation.type ),\r
+ operation );\r
+\r
+ status = IOT_MQTT_NETWORK_ERROR;\r
+ }\r
+ else\r
+ {\r
+ IotLogInfo( "(MQTT connection %p, %s operation %p) Waiting for operation completion.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( operation->u.operation.type ),\r
+ operation );\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* Only wait on an operation if the MQTT connection is active. */\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ if( IotSemaphore_TimedWait( &( operation->u.operation.notify.waitSemaphore ),\r
+ timeoutMs ) == false )\r
+ {\r
+ status = IOT_MQTT_TIMEOUT;\r
+\r
+ /* Attempt to cancel the job of the timed out operation. */\r
+ ( void ) _IotMqtt_DecrementOperationReferences( operation, true );\r
+\r
+ /* Clean up lingering subscriptions from a timed-out SUBSCRIBE. */\r
+ if( operation->u.operation.type == IOT_MQTT_SUBSCRIBE )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p, SUBSCRIBE operation %p) Cleaning up"\r
+ " subscriptions of timed-out SUBSCRIBE.",\r
+ pMqttConnection,\r
+ operation );\r
+\r
+ _IotMqtt_RemoveSubscriptionByPacket( pMqttConnection,\r
+ operation->u.operation.packetIdentifier,\r
+ -1 );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* Retrieve the status of the completed operation. */\r
+ status = operation->u.operation.status;\r
+ }\r
+\r
+ IotLogInfo( "(MQTT connection %p, %s operation %p) Wait complete with result %s.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( operation->u.operation.type ),\r
+ operation,\r
+ IotMqtt_strerror( status ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Wait is finished; decrement operation reference count. */\r
+ if( _IotMqtt_DecrementOperationReferences( operation, false ) == true )\r
+ {\r
+ _IotMqtt_DestroyOperation( operation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+const char * IotMqtt_strerror( IotMqttError_t status )\r
+{\r
+ const char * pMessage = NULL;\r
+\r
+ switch( status )\r
+ {\r
+ case IOT_MQTT_SUCCESS:\r
+ pMessage = "SUCCESS";\r
+ break;\r
+\r
+ case IOT_MQTT_STATUS_PENDING:\r
+ pMessage = "PENDING";\r
+ break;\r
+\r
+ case IOT_MQTT_INIT_FAILED:\r
+ pMessage = "INITIALIZATION FAILED";\r
+ break;\r
+\r
+ case IOT_MQTT_BAD_PARAMETER:\r
+ pMessage = "BAD PARAMETER";\r
+ break;\r
+\r
+ case IOT_MQTT_NO_MEMORY:\r
+ pMessage = "NO MEMORY";\r
+ break;\r
+\r
+ case IOT_MQTT_NETWORK_ERROR:\r
+ pMessage = "NETWORK ERROR";\r
+ break;\r
+\r
+ case IOT_MQTT_SCHEDULING_ERROR:\r
+ pMessage = "SCHEDULING ERROR";\r
+ break;\r
+\r
+ case IOT_MQTT_BAD_RESPONSE:\r
+ pMessage = "BAD RESPONSE RECEIVED";\r
+ break;\r
+\r
+ case IOT_MQTT_TIMEOUT:\r
+ pMessage = "TIMEOUT";\r
+ break;\r
+\r
+ case IOT_MQTT_SERVER_REFUSED:\r
+ pMessage = "SERVER REFUSED";\r
+ break;\r
+\r
+ case IOT_MQTT_RETRY_NO_RESPONSE:\r
+ pMessage = "NO RESPONSE";\r
+ break;\r
+\r
+ default:\r
+ pMessage = "INVALID STATUS";\r
+ break;\r
+ }\r
+\r
+ return pMessage;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+const char * IotMqtt_OperationType( IotMqttOperationType_t operation )\r
+{\r
+ const char * pMessage = NULL;\r
+\r
+ switch( operation )\r
+ {\r
+ case IOT_MQTT_CONNECT:\r
+ pMessage = "CONNECT";\r
+ break;\r
+\r
+ case IOT_MQTT_PUBLISH_TO_SERVER:\r
+ pMessage = "PUBLISH";\r
+ break;\r
+\r
+ case IOT_MQTT_PUBACK:\r
+ pMessage = "PUBACK";\r
+ break;\r
+\r
+ case IOT_MQTT_SUBSCRIBE:\r
+ pMessage = "SUBSCRIBE";\r
+ break;\r
+\r
+ case IOT_MQTT_UNSUBSCRIBE:\r
+ pMessage = "UNSUBSCRIBE";\r
+ break;\r
+\r
+ case IOT_MQTT_PINGREQ:\r
+ pMessage = "PINGREQ";\r
+ break;\r
+\r
+ case IOT_MQTT_DISCONNECT:\r
+ pMessage = "DISCONNECT";\r
+ break;\r
+\r
+ default:\r
+ pMessage = "INVALID OPERATION";\r
+ break;\r
+ }\r
+\r
+ return pMessage;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/* Provide access to internal functions and variables if testing. */\r
+#if IOT_BUILD_TESTS == 1\r
+ #include "iot_test_access_mqtt_api.c"\r
+#endif\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_network.c\r
+ * @brief Implements functions involving transport layer connections.\r
+ */\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Standard includes. */\r
+#include <string.h>\r
+\r
+/* Error handling include. */\r
+#include "private/iot_error.h"\r
+\r
+/* MQTT internal include. */\r
+#include "private/iot_mqtt_internal.h"\r
+\r
+/* Platform layer includes. */\r
+#include "platform/iot_threads.h"\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Check if an incoming packet type is valid.\r
+ *\r
+ * @param[in] packetType The packet type to check.\r
+ *\r
+ * @return `true` if the packet type is valid; `false` otherwise.\r
+ */\r
+static bool _incomingPacketValid( uint8_t packetType );\r
+\r
+/**\r
+ * @brief Get an incoming MQTT packet from the network.\r
+ *\r
+ * @param[in] pNetworkConnection Network connection to use for receive, which\r
+ * may be different from the network connection associated with the MQTT connection.\r
+ * @param[in] pMqttConnection The associated MQTT connection.\r
+ * @param[out] pIncomingPacket Output parameter for the incoming packet.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS, #IOT_MQTT_NO_MEMORY or #IOT_MQTT_BAD_RESPONSE.\r
+ */\r
+static IotMqttError_t _getIncomingPacket( void * pNetworkConnection,\r
+ const _mqttConnection_t * pMqttConnection,\r
+ _mqttPacket_t * pIncomingPacket );\r
+\r
+/**\r
+ * @brief Deserialize a packet received from the network.\r
+ *\r
+ * @param[in] pMqttConnection The associated MQTT connection.\r
+ * @param[in] pIncomingPacket The packet received from the network.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS, #IOT_MQTT_NO_MEMORY, #IOT_MQTT_NETWORK_ERROR,\r
+ * #IOT_MQTT_SCHEDULING_ERROR, #IOT_MQTT_BAD_RESPONSE, or #IOT_MQTT_SERVER_REFUSED.\r
+ */\r
+static IotMqttError_t _deserializeIncomingPacket( _mqttConnection_t * pMqttConnection,\r
+ _mqttPacket_t * pIncomingPacket );\r
+\r
+/**\r
+ * @brief Send a PUBACK for a received QoS 1 PUBLISH packet.\r
+ *\r
+ * @param[in] pMqttConnection Which connection the PUBACK should be sent over.\r
+ * @param[in] packetIdentifier Which packet identifier to include in PUBACK.\r
+ */\r
+static void _sendPuback( _mqttConnection_t * pMqttConnection,\r
+ uint16_t packetIdentifier );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _incomingPacketValid( uint8_t packetType )\r
+{\r
+ bool status = true;\r
+\r
+ /* Check packet type. Mask out lower bits to ignore flags. */\r
+ switch( packetType & 0xf0 )\r
+ {\r
+ /* Valid incoming packet types. */\r
+ case MQTT_PACKET_TYPE_CONNACK:\r
+ case MQTT_PACKET_TYPE_PUBLISH:\r
+ case MQTT_PACKET_TYPE_PUBACK:\r
+ case MQTT_PACKET_TYPE_SUBACK:\r
+ case MQTT_PACKET_TYPE_UNSUBACK:\r
+ case MQTT_PACKET_TYPE_PINGRESP:\r
+ break;\r
+\r
+ /* Any other packet type is invalid. */\r
+ default:\r
+ status = false;\r
+ break;\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static IotMqttError_t _getIncomingPacket( void * pNetworkConnection,\r
+ const _mqttConnection_t * pMqttConnection,\r
+ _mqttPacket_t * pIncomingPacket )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ size_t dataBytesRead = 0;\r
+\r
+ /* Default functions for retrieving packet type and length. */\r
+ uint8_t ( * getPacketType )( void *,\r
+ const IotNetworkInterface_t * ) = _IotMqtt_GetPacketType;\r
+ size_t ( * getRemainingLength )( void *,\r
+ const IotNetworkInterface_t * ) = _IotMqtt_GetRemainingLength;\r
+\r
+ /* No buffer for remaining data should be allocated. */\r
+ IotMqtt_Assert( pIncomingPacket->pRemainingData == NULL );\r
+ IotMqtt_Assert( pIncomingPacket->remainingLength == 0 );\r
+\r
+ /* Choose packet type and length functions. */\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->getPacketType != NULL )\r
+ {\r
+ getPacketType = pMqttConnection->pSerializer->getPacketType;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pMqttConnection->pSerializer->getRemainingLength != NULL )\r
+ {\r
+ getRemainingLength = pMqttConnection->pSerializer->getRemainingLength;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Read the packet type, which is the first byte available. */\r
+ pIncomingPacket->type = getPacketType( pNetworkConnection,\r
+ pMqttConnection->pNetworkInterface );\r
+\r
+ /* Check that the incoming packet type is valid. */\r
+ if( _incomingPacketValid( pIncomingPacket->type ) == false )\r
+ {\r
+ IotLogError( "(MQTT connection %p) Unknown packet type %02x received.",\r
+ pMqttConnection,\r
+ pIncomingPacket->type );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Read the remaining length. */\r
+ pIncomingPacket->remainingLength = getRemainingLength( pNetworkConnection,\r
+ pMqttConnection->pNetworkInterface );\r
+\r
+ if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Allocate a buffer for the remaining data and read the data. */\r
+ if( pIncomingPacket->remainingLength > 0 )\r
+ {\r
+ pIncomingPacket->pRemainingData = IotMqtt_MallocMessage( pIncomingPacket->remainingLength );\r
+\r
+ if( pIncomingPacket->pRemainingData == NULL )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ dataBytesRead = pMqttConnection->pNetworkInterface->receive( pNetworkConnection,\r
+ pIncomingPacket->pRemainingData,\r
+ pIncomingPacket->remainingLength );\r
+\r
+ if( dataBytesRead != pIncomingPacket->remainingLength )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Clean up on error. */\r
+ IOT_FUNCTION_CLEANUP_BEGIN();\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ if( pIncomingPacket->pRemainingData != NULL )\r
+ {\r
+ IotMqtt_FreeMessage( pIncomingPacket->pRemainingData );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_CLEANUP_END();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static IotMqttError_t _deserializeIncomingPacket( _mqttConnection_t * pMqttConnection,\r
+ _mqttPacket_t * pIncomingPacket )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_STATUS_PENDING;\r
+ _mqttOperation_t * pOperation = NULL;\r
+\r
+ /* Deserializer function. */\r
+ IotMqttError_t ( * deserialize )( _mqttPacket_t * ) = NULL;\r
+\r
+ /* A buffer for remaining data must be allocated if remaining length is not 0. */\r
+ IotMqtt_Assert( ( pIncomingPacket->remainingLength > 0 ) ==\r
+ ( pIncomingPacket->pRemainingData != NULL ) );\r
+\r
+ /* Only valid packets should be given to this function. */\r
+ IotMqtt_Assert( _incomingPacketValid( pIncomingPacket->type ) == true );\r
+\r
+ /* Mask out the low bits of packet type to ignore flags. */\r
+ switch( ( pIncomingPacket->type & 0xf0 ) )\r
+ {\r
+ case MQTT_PACKET_TYPE_CONNACK:\r
+ IotLogDebug( "(MQTT connection %p) CONNACK in data stream.", pMqttConnection );\r
+\r
+ /* Choose CONNACK deserializer. */\r
+ deserialize = _IotMqtt_DeserializeConnack;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->deserialize.connack != NULL )\r
+ {\r
+ deserialize = pMqttConnection->pSerializer->deserialize.connack;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Deserialize CONNACK and notify of result. */\r
+ status = deserialize( pIncomingPacket );\r
+ pOperation = _IotMqtt_FindOperation( pMqttConnection,\r
+ IOT_MQTT_CONNECT,\r
+ NULL );\r
+\r
+ if( pOperation != NULL )\r
+ {\r
+ pOperation->u.operation.status = status;\r
+ _IotMqtt_Notify( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ break;\r
+\r
+ case MQTT_PACKET_TYPE_PUBLISH:\r
+ IotLogDebug( "(MQTT connection %p) PUBLISH in data stream.", pMqttConnection );\r
+\r
+ /* Allocate memory to handle the incoming PUBLISH. */\r
+ pOperation = IotMqtt_MallocOperation( sizeof( _mqttOperation_t ) );\r
+\r
+ if( pOperation == NULL )\r
+ {\r
+ IotLogWarn( "Failed to allocate memory for incoming PUBLISH." );\r
+ status = IOT_MQTT_NO_MEMORY;\r
+\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ /* Set the members of the incoming PUBLISH operation. */\r
+ ( void ) memset( pOperation, 0x00, sizeof( _mqttOperation_t ) );\r
+ pOperation->incomingPublish = true;\r
+ pOperation->pMqttConnection = pMqttConnection;\r
+ pIncomingPacket->u.pIncomingPublish = pOperation;\r
+ }\r
+\r
+ /* Choose a PUBLISH deserializer. */\r
+ deserialize = _IotMqtt_DeserializePublish;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->deserialize.publish != NULL )\r
+ {\r
+ deserialize = pMqttConnection->pSerializer->deserialize.publish;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Deserialize incoming PUBLISH. */\r
+ status = deserialize( pIncomingPacket );\r
+\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ /* Send a PUBACK for QoS 1 PUBLISH. */\r
+ if( pOperation->u.publish.publishInfo.qos == IOT_MQTT_QOS_1 )\r
+ {\r
+ _sendPuback( pMqttConnection, pIncomingPacket->packetIdentifier );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Transfer ownership of the received MQTT packet to the PUBLISH operation. */\r
+ pOperation->u.publish.pReceivedData = pIncomingPacket->pRemainingData;\r
+ pIncomingPacket->pRemainingData = NULL;\r
+\r
+ /* Add the PUBLISH to the list of operations pending processing. */\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+ IotListDouble_InsertHead( &( pMqttConnection->pendingProcessing ),\r
+ &( pOperation->link ) );\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* Increment the MQTT connection reference count before scheduling an\r
+ * incoming PUBLISH. */\r
+ if( _IotMqtt_IncrementConnectionReferences( pMqttConnection ) == true )\r
+ {\r
+ /* Schedule PUBLISH for callback invocation. */\r
+ status = _IotMqtt_ScheduleOperation( pOperation, _IotMqtt_ProcessIncomingPublish, 0 );\r
+ }\r
+ else\r
+ {\r
+ status = IOT_MQTT_NETWORK_ERROR;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Free PUBLISH operation on error. */\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ /* Check ownership of the received MQTT packet. */\r
+ if( pOperation->u.publish.pReceivedData != NULL )\r
+ {\r
+ /* Retrieve the pointer MQTT packet pointer so it may be freed later. */\r
+ IotMqtt_Assert( pIncomingPacket->pRemainingData == NULL );\r
+ pIncomingPacket->pRemainingData = ( uint8_t * ) pOperation->u.publish.pReceivedData;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Remove operation from pending processing list. */\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ if( IotLink_IsLinked( &( pOperation->link ) ) == true )\r
+ {\r
+ IotListDouble_Remove( &( pOperation->link ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ IotMqtt_Assert( pOperation != NULL );\r
+ IotMqtt_FreeOperation( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ break;\r
+\r
+ case MQTT_PACKET_TYPE_PUBACK:\r
+ IotLogDebug( "(MQTT connection %p) PUBACK in data stream.", pMqttConnection );\r
+\r
+ /* Choose PUBACK deserializer. */\r
+ deserialize = _IotMqtt_DeserializePuback;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->deserialize.puback != NULL )\r
+ {\r
+ deserialize = pMqttConnection->pSerializer->deserialize.puback;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Deserialize PUBACK and notify of result. */\r
+ status = deserialize( pIncomingPacket );\r
+ pOperation = _IotMqtt_FindOperation( pMqttConnection,\r
+ IOT_MQTT_PUBLISH_TO_SERVER,\r
+ &( pIncomingPacket->packetIdentifier ) );\r
+\r
+ if( pOperation != NULL )\r
+ {\r
+ pOperation->u.operation.status = status;\r
+ _IotMqtt_Notify( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ break;\r
+\r
+ case MQTT_PACKET_TYPE_SUBACK:\r
+ IotLogDebug( "(MQTT connection %p) SUBACK in data stream.", pMqttConnection );\r
+\r
+ /* Choose SUBACK deserializer. */\r
+ deserialize = _IotMqtt_DeserializeSuback;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->deserialize.suback != NULL )\r
+ {\r
+ deserialize = pMqttConnection->pSerializer->deserialize.suback;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Deserialize SUBACK and notify of result. */\r
+ pIncomingPacket->u.pMqttConnection = pMqttConnection;\r
+ status = deserialize( pIncomingPacket );\r
+ pOperation = _IotMqtt_FindOperation( pMqttConnection,\r
+ IOT_MQTT_SUBSCRIBE,\r
+ &( pIncomingPacket->packetIdentifier ) );\r
+\r
+ if( pOperation != NULL )\r
+ {\r
+ pOperation->u.operation.status = status;\r
+ _IotMqtt_Notify( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ break;\r
+\r
+ case MQTT_PACKET_TYPE_UNSUBACK:\r
+ IotLogDebug( "(MQTT connection %p) UNSUBACK in data stream.", pMqttConnection );\r
+\r
+ /* Choose UNSUBACK deserializer. */\r
+ deserialize = _IotMqtt_DeserializeUnsuback;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->deserialize.unsuback != NULL )\r
+ {\r
+ deserialize = pMqttConnection->pSerializer->deserialize.unsuback;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Deserialize UNSUBACK and notify of result. */\r
+ status = deserialize( pIncomingPacket );\r
+ pOperation = _IotMqtt_FindOperation( pMqttConnection,\r
+ IOT_MQTT_UNSUBSCRIBE,\r
+ &( pIncomingPacket->packetIdentifier ) );\r
+\r
+ if( pOperation != NULL )\r
+ {\r
+ pOperation->u.operation.status = status;\r
+ _IotMqtt_Notify( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ break;\r
+\r
+ default:\r
+ /* The only remaining valid type is PINGRESP. */\r
+ IotMqtt_Assert( ( pIncomingPacket->type & 0xf0 ) == MQTT_PACKET_TYPE_PINGRESP );\r
+ IotLogDebug( "(MQTT connection %p) PINGRESP in data stream.", pMqttConnection );\r
+\r
+ /* Choose PINGRESP deserializer. */\r
+ deserialize = _IotMqtt_DeserializePingresp;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->deserialize.pingresp != NULL )\r
+ {\r
+ deserialize = pMqttConnection->pSerializer->deserialize.pingresp;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Deserialize PINGRESP. */\r
+ status = deserialize( pIncomingPacket );\r
+\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ if( pMqttConnection->keepAliveFailure == false )\r
+ {\r
+ IotLogWarn( "(MQTT connection %p) Unexpected PINGRESP received.",\r
+ pMqttConnection );\r
+ }\r
+ else\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) PINGRESP successfully parsed.",\r
+ pMqttConnection );\r
+\r
+ pMqttConnection->keepAliveFailure = false;\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogError( "(MQTT connection %p) Packet parser status %s.",\r
+ pMqttConnection,\r
+ IotMqtt_strerror( status ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void _sendPuback( _mqttConnection_t * pMqttConnection,\r
+ uint16_t packetIdentifier )\r
+{\r
+ IotMqttError_t serializeStatus = IOT_MQTT_SUCCESS;\r
+ uint8_t * pPuback = NULL;\r
+ size_t pubackSize = 0, bytesSent = 0;\r
+\r
+ /* Default PUBACK serializer and free packet functions. */\r
+ IotMqttError_t ( * serializePuback )( uint16_t,\r
+ uint8_t **,\r
+ size_t * ) = _IotMqtt_SerializePuback;\r
+ void ( * freePacket )( uint8_t * ) = _IotMqtt_FreePacket;\r
+\r
+ IotLogDebug( "(MQTT connection %p) Sending PUBACK for received PUBLISH %hu.",\r
+ pMqttConnection,\r
+ packetIdentifier );\r
+\r
+ /* Choose PUBACK serializer and free packet functions. */\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->serialize.puback != NULL )\r
+ {\r
+ serializePuback = pMqttConnection->pSerializer->serialize.puback;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->freePacket != NULL )\r
+ {\r
+ freePacket = pMqttConnection->pSerializer->freePacket;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Generate a PUBACK packet from the packet identifier. */\r
+ serializeStatus = serializePuback( packetIdentifier,\r
+ &pPuback,\r
+ &pubackSize );\r
+\r
+ if( serializeStatus != IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogWarn( "(MQTT connection %p) Failed to generate PUBACK packet for "\r
+ "received PUBLISH %hu.",\r
+ pMqttConnection,\r
+ packetIdentifier );\r
+ }\r
+ else\r
+ {\r
+ bytesSent = pMqttConnection->pNetworkInterface->send( pMqttConnection->pNetworkConnection,\r
+ pPuback,\r
+ pubackSize );\r
+\r
+ if( bytesSent != pubackSize )\r
+ {\r
+ IotLogWarn( "(MQTT connection %p) Failed to send PUBACK for received"\r
+ " PUBLISH %hu.",\r
+ pMqttConnection,\r
+ packetIdentifier );\r
+ }\r
+ else\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) PUBACK for received PUBLISH %hu sent.",\r
+ pMqttConnection,\r
+ packetIdentifier );\r
+ }\r
+\r
+ freePacket( pPuback );\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+bool _IotMqtt_GetNextByte( void * pNetworkConnection,\r
+ const IotNetworkInterface_t * pNetworkInterface,\r
+ uint8_t * pIncomingByte )\r
+{\r
+ bool status = false;\r
+ uint8_t incomingByte = 0;\r
+ size_t bytesReceived = 0;\r
+\r
+ /* Attempt to read 1 byte. */\r
+ bytesReceived = pNetworkInterface->receive( pNetworkConnection,\r
+ &incomingByte,\r
+ 1 );\r
+\r
+ /* Set the output parameter and return success if 1 byte was read. */\r
+ if( bytesReceived == 1 )\r
+ {\r
+ *pIncomingByte = incomingByte;\r
+ status = true;\r
+ }\r
+ else\r
+ {\r
+ /* Network receive must return 0 on failure. */\r
+ IotMqtt_Assert( bytesReceived == 0 );\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_CloseNetworkConnection( IotMqttDisconnectReason_t disconnectReason,\r
+ _mqttConnection_t * pMqttConnection )\r
+{\r
+ IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;\r
+ IotNetworkError_t closeStatus = IOT_NETWORK_SUCCESS;\r
+ IotMqttCallbackParam_t callbackParam = { .u.message = { 0 } };\r
+\r
+ /* Mark the MQTT connection as disconnected and the keep-alive as failed. */\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+ pMqttConnection->disconnected = true;\r
+ pMqttConnection->keepAliveFailure = true;\r
+\r
+ if( pMqttConnection->keepAliveMs != 0 )\r
+ {\r
+ /* Keep-alive must have a PINGREQ allocated. */\r
+ IotMqtt_Assert( pMqttConnection->pPingreqPacket != NULL );\r
+ IotMqtt_Assert( pMqttConnection->pingreqPacketSize != 0 );\r
+\r
+ /* PINGREQ provides a reference to the connection, so reference count must\r
+ * be nonzero. */\r
+ IotMqtt_Assert( pMqttConnection->references > 0 );\r
+\r
+ /* Attempt to cancel the keep-alive job. */\r
+ taskPoolStatus = IotTaskPool_TryCancel( IOT_SYSTEM_TASKPOOL,\r
+ pMqttConnection->keepAliveJob,\r
+ NULL );\r
+\r
+ /* If the keep-alive job was not canceled, it must be already executing.\r
+ * Any other return value is invalid. */\r
+ IotMqtt_Assert( ( taskPoolStatus == IOT_TASKPOOL_SUCCESS ) ||\r
+ ( taskPoolStatus == IOT_TASKPOOL_CANCEL_FAILED ) );\r
+\r
+ /* Clean up keep-alive if its job was successfully canceled. Otherwise,\r
+ * the executing keep-alive job will clean up itself. */\r
+ if( taskPoolStatus == IOT_TASKPOOL_SUCCESS )\r
+ {\r
+ /* Clean up PINGREQ packet and job. */\r
+ _IotMqtt_FreePacket( pMqttConnection->pPingreqPacket );\r
+\r
+ /* Clear data about the keep-alive. */\r
+ pMqttConnection->keepAliveMs = 0;\r
+ pMqttConnection->pPingreqPacket = NULL;\r
+ pMqttConnection->pingreqPacketSize = 0;\r
+\r
+ /* Keep-alive is cleaned up; decrement reference count. Since this\r
+ * function must be followed with a call to DISCONNECT, a check to\r
+ * destroy the connection is not done here. */\r
+ pMqttConnection->references--;\r
+\r
+ IotLogDebug( "(MQTT connection %p) Keep-alive job canceled and cleaned up.",\r
+ pMqttConnection );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* Close the network connection. */\r
+ if( pMqttConnection->pNetworkInterface->close != NULL )\r
+ {\r
+ closeStatus = pMqttConnection->pNetworkInterface->close( pMqttConnection->pNetworkConnection );\r
+\r
+ if( closeStatus == IOT_NETWORK_SUCCESS )\r
+ {\r
+ IotLogInfo( "(MQTT connection %p) Network connection closed.", pMqttConnection );\r
+ }\r
+ else\r
+ {\r
+ IotLogWarn( "(MQTT connection %p) Failed to close network connection, error %d.",\r
+ pMqttConnection,\r
+ closeStatus );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ IotLogWarn( "(MQTT connection %p) No network close function was set. Network connection"\r
+ " not closed.", pMqttConnection );\r
+ }\r
+\r
+ /* Invoke the disconnect callback. */\r
+ if( pMqttConnection->disconnectCallback.function != NULL )\r
+ {\r
+ /* Set the members of the callback parameter. */\r
+ callbackParam.mqttConnection = pMqttConnection;\r
+ callbackParam.u.disconnectReason = disconnectReason;\r
+\r
+ pMqttConnection->disconnectCallback.function( pMqttConnection->disconnectCallback.pCallbackContext,\r
+ &callbackParam );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void IotMqtt_ReceiveCallback( void * pNetworkConnection,\r
+ void * pReceiveContext )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_SUCCESS;\r
+ _mqttPacket_t incomingPacket = { .u.pMqttConnection = NULL };\r
+\r
+ /* Cast context to correct type. */\r
+ _mqttConnection_t * pMqttConnection = ( _mqttConnection_t * ) pReceiveContext;\r
+\r
+ /* Read an MQTT packet from the network. */\r
+ status = _getIncomingPacket( pNetworkConnection,\r
+ pMqttConnection,\r
+ &incomingPacket );\r
+\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ /* Deserialize the received packet. */\r
+ status = _deserializeIncomingPacket( pMqttConnection,\r
+ &incomingPacket );\r
+\r
+ /* Free any buffers allocated for the MQTT packet. */\r
+ if( incomingPacket.pRemainingData != NULL )\r
+ {\r
+ IotMqtt_FreeMessage( incomingPacket.pRemainingData );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Close the network connection on a bad response. */\r
+ if( status == IOT_MQTT_BAD_RESPONSE )\r
+ {\r
+ IotLogError( "(MQTT connection %p) Error processing incoming data. Closing connection.",\r
+ pMqttConnection );\r
+\r
+ _IotMqtt_CloseNetworkConnection( IOT_MQTT_BAD_PACKET_RECEIVED,\r
+ pMqttConnection );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_operation.c\r
+ * @brief Implements functions that process MQTT operations.\r
+ */\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Standard includes. */\r
+#include <string.h>\r
+\r
+/* Error handling include. */\r
+#include "private/iot_error.h"\r
+\r
+/* MQTT internal include. */\r
+#include "private/iot_mqtt_internal.h"\r
+\r
+/* Platform layer includes. */\r
+#include "platform/iot_clock.h"\r
+#include "platform/iot_threads.h"\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief First parameter to #_mqttOperation_match.\r
+ */\r
+typedef struct _operationMatchParam\r
+{\r
+ IotMqttOperationType_t type; /**< @brief The type of operation to look for. */\r
+ const uint16_t * pPacketIdentifier; /**< @brief The packet identifier associated with the operation.\r
+ * Set to `NULL` to ignore packet identifier. */\r
+} _operationMatchParam_t;\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Match an MQTT operation by type and packet identifier.\r
+ *\r
+ * @param[in] pOperationLink Pointer to the link member of an #_mqttOperation_t.\r
+ * @param[in] pMatch Pointer to an #_operationMatchParam_t.\r
+ *\r
+ * @return `true` if the operation matches the parameters in `pArgument`; `false`\r
+ * otherwise.\r
+ */\r
+static bool _mqttOperation_match( const IotLink_t * pOperationLink,\r
+ void * pMatch );\r
+\r
+/**\r
+ * @brief Check if an operation with retry has exceeded its retry limit.\r
+ *\r
+ * If a PUBLISH operation is available for retry, this function also sets any\r
+ * necessary DUP flags.\r
+ *\r
+ * @param[in] pOperation The operation to check.\r
+ *\r
+ * @return `true` if the operation may be retried; `false` otherwise.\r
+ */\r
+static bool _checkRetryLimit( _mqttOperation_t * pOperation );\r
+\r
+/**\r
+ * @brief Schedule the next send of an operation with retry.\r
+ *\r
+ * @param[in] pOperation The operation to schedule.\r
+ *\r
+ * @return `true` if the reschedule succeeded; `false` otherwise.\r
+ */\r
+static bool _scheduleNextRetry( _mqttOperation_t * pOperation );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _mqttOperation_match( const IotLink_t * pOperationLink,\r
+ void * pMatch )\r
+{\r
+ bool match = false;\r
+\r
+ /* Because this function is called from a container function, the given link\r
+ * must never be NULL. */\r
+ IotMqtt_Assert( pOperationLink != NULL );\r
+\r
+ _mqttOperation_t * pOperation = IotLink_Container( _mqttOperation_t,\r
+ pOperationLink,\r
+ link );\r
+ _operationMatchParam_t * pParam = ( _operationMatchParam_t * ) pMatch;\r
+\r
+ /* Check for matching operations. */\r
+ if( pParam->type == pOperation->u.operation.type )\r
+ {\r
+ /* Check for matching packet identifiers. */\r
+ if( pParam->pPacketIdentifier == NULL )\r
+ {\r
+ match = true;\r
+ }\r
+ else\r
+ {\r
+ match = ( *( pParam->pPacketIdentifier ) == pOperation->u.operation.packetIdentifier );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return match;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _checkRetryLimit( _mqttOperation_t * pOperation )\r
+{\r
+ _mqttConnection_t * pMqttConnection = pOperation->pMqttConnection;\r
+ bool status = true;\r
+\r
+ /* Choose a set DUP function. */\r
+ void ( * publishSetDup )( uint8_t *,\r
+ uint8_t *,\r
+ uint16_t * ) = _IotMqtt_PublishSetDup;\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->serialize.publishSetDup != NULL )\r
+ {\r
+ publishSetDup = pMqttConnection->pSerializer->serialize.publishSetDup;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ /* Only PUBLISH may be retried. */\r
+ IotMqtt_Assert( pOperation->u.operation.type == IOT_MQTT_PUBLISH_TO_SERVER );\r
+\r
+ /* Check if the retry limit is exhausted. */\r
+ if( pOperation->u.operation.retry.count > pOperation->u.operation.retry.limit )\r
+ {\r
+ /* The retry count may be at most one more than the retry limit, which\r
+ * accounts for the final check for a PUBACK. */\r
+ IotMqtt_Assert( pOperation->u.operation.retry.count == pOperation->u.operation.retry.limit + 1 );\r
+\r
+ IotLogDebug( "(MQTT connection %p, PUBLISH operation %p) No response received after %lu retries.",\r
+ pMqttConnection,\r
+ pOperation,\r
+ pOperation->u.operation.retry.limit );\r
+\r
+ status = false;\r
+ }\r
+ /* Check if this is the first retry. */\r
+ else if( pOperation->u.operation.retry.count == 1 )\r
+ {\r
+ /* Always set the DUP flag on the first retry. */\r
+ publishSetDup( pOperation->u.operation.pMqttPacket,\r
+ pOperation->u.operation.pPacketIdentifierHigh,\r
+ &( pOperation->u.operation.packetIdentifier ) );\r
+ }\r
+ else\r
+ {\r
+ /* In AWS IoT MQTT mode, the DUP flag (really a change to the packet\r
+ * identifier) must be reset on every retry. */\r
+ if( pMqttConnection->awsIotMqttMode == true )\r
+ {\r
+ publishSetDup( pOperation->u.operation.pMqttPacket,\r
+ pOperation->u.operation.pPacketIdentifierHigh,\r
+ &( pOperation->u.operation.packetIdentifier ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _scheduleNextRetry( _mqttOperation_t * pOperation )\r
+{\r
+ bool firstRetry = false;\r
+ uint32_t scheduleDelay = 0;\r
+ IotMqttError_t status = IOT_MQTT_STATUS_PENDING;\r
+ _mqttConnection_t * pMqttConnection = pOperation->pMqttConnection;\r
+\r
+ /* This function should never be called with retry count greater than\r
+ * retry limit. */\r
+ IotMqtt_Assert( pOperation->u.operation.retry.count <= pOperation->u.operation.retry.limit );\r
+\r
+ /* Increment the retry count. */\r
+ ( pOperation->u.operation.retry.count )++;\r
+\r
+ /* Check for a response shortly for the final retry. Otherwise, calculate the\r
+ * next retry period. */\r
+ if( pOperation->u.operation.retry.count > pOperation->u.operation.retry.limit )\r
+ {\r
+ scheduleDelay = IOT_MQTT_RESPONSE_WAIT_MS;\r
+\r
+ IotLogDebug( "(MQTT connection %p, PUBLISH operation %p) Final retry was sent. Will check "\r
+ "for response in %d ms.",\r
+ pMqttConnection,\r
+ pOperation,\r
+ IOT_MQTT_RESPONSE_WAIT_MS );\r
+ }\r
+ else\r
+ {\r
+ scheduleDelay = pOperation->u.operation.retry.nextPeriod;\r
+\r
+ /* Double the retry period, subject to a ceiling value. */\r
+ pOperation->u.operation.retry.nextPeriod *= 2;\r
+\r
+ if( pOperation->u.operation.retry.nextPeriod > IOT_MQTT_RETRY_MS_CEILING )\r
+ {\r
+ pOperation->u.operation.retry.nextPeriod = IOT_MQTT_RETRY_MS_CEILING;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotLogDebug( "(MQTT connection %p, PUBLISH operation %p) Scheduling retry %lu of %lu in %lu ms.",\r
+ pMqttConnection,\r
+ pOperation,\r
+ ( unsigned long ) pOperation->u.operation.retry.count,\r
+ ( unsigned long ) pOperation->u.operation.retry.limit,\r
+ ( unsigned long ) scheduleDelay );\r
+\r
+ /* Check if this is the first retry. */\r
+ firstRetry = ( pOperation->u.operation.retry.count == 1 );\r
+\r
+ /* On the first retry, the PUBLISH will be moved from the pending processing\r
+ * list to the pending responses list. Lock the connection references mutex\r
+ * to manipulate the lists. */\r
+ if( firstRetry == true )\r
+ {\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ /* Reschedule the PUBLISH for another send. */\r
+ status = _IotMqtt_ScheduleOperation( pOperation,\r
+ _IotMqtt_ProcessSend,\r
+ scheduleDelay );\r
+\r
+ /* Check for successful reschedule. */\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ /* Move a successfully rescheduled PUBLISH from the pending processing\r
+ * list to the pending responses list on the first retry. */\r
+ if( firstRetry == true )\r
+ {\r
+ /* Operation must be linked. */\r
+ IotMqtt_Assert( IotLink_IsLinked( &( pOperation->link ) ) == true );\r
+\r
+ /* Transfer to pending response list. */\r
+ IotListDouble_Remove( &( pOperation->link ) );\r
+ IotListDouble_InsertHead( &( pMqttConnection->pendingResponse ),\r
+ &( pOperation->link ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* The references mutex only needs to be unlocked on the first retry, since\r
+ * only the first retry manipulates the connection lists. */\r
+ if( firstRetry == true )\r
+ {\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return( status == IOT_MQTT_SUCCESS );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_CreateOperation( _mqttConnection_t * pMqttConnection,\r
+ uint32_t flags,\r
+ const IotMqttCallbackInfo_t * pCallbackInfo,\r
+ _mqttOperation_t ** pNewOperation )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ bool decrementOnError = false;\r
+ _mqttOperation_t * pOperation = NULL;\r
+ bool waitable = ( ( flags & IOT_MQTT_FLAG_WAITABLE ) == IOT_MQTT_FLAG_WAITABLE );\r
+\r
+ /* If the waitable flag is set, make sure that there's no callback. */\r
+ if( waitable == true )\r
+ {\r
+ if( pCallbackInfo != NULL )\r
+ {\r
+ IotLogError( "Callback should not be set for a waitable operation." );\r
+\r
+ return IOT_MQTT_BAD_PARAMETER;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotLogDebug( "(MQTT connection %p) Creating new operation record.",\r
+ pMqttConnection );\r
+\r
+ /* Increment the reference count for the MQTT connection when creating a new\r
+ * operation. */\r
+ if( _IotMqtt_IncrementConnectionReferences( pMqttConnection ) == false )\r
+ {\r
+ IotLogError( "(MQTT connection %p) New operation record cannot be created"\r
+ " for a closed connection",\r
+ pMqttConnection );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NETWORK_ERROR );\r
+ }\r
+ else\r
+ {\r
+ /* Reference count will need to be decremented on error. */\r
+ decrementOnError = true;\r
+ }\r
+\r
+ /* Allocate memory for a new operation. */\r
+ pOperation = IotMqtt_MallocOperation( sizeof( _mqttOperation_t ) );\r
+\r
+ if( pOperation == NULL )\r
+ {\r
+ IotLogError( "(MQTT connection %p) Failed to allocate memory for new "\r
+ "operation record.",\r
+ pMqttConnection );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );\r
+ }\r
+ else\r
+ {\r
+ /* Clear the operation data. */\r
+ ( void ) memset( pOperation, 0x00, sizeof( _mqttOperation_t ) );\r
+\r
+ /* Initialize some members of the new operation. */\r
+ pOperation->pMqttConnection = pMqttConnection;\r
+ pOperation->u.operation.jobReference = 1;\r
+ pOperation->u.operation.flags = flags;\r
+ pOperation->u.operation.status = IOT_MQTT_STATUS_PENDING;\r
+ }\r
+\r
+ /* Check if the waitable flag is set. If it is, create a semaphore to\r
+ * wait on. */\r
+ if( waitable == true )\r
+ {\r
+ /* Create a semaphore to wait on for a waitable operation. */\r
+ if( IotSemaphore_Create( &( pOperation->u.operation.notify.waitSemaphore ), 0, 1 ) == false )\r
+ {\r
+ IotLogError( "(MQTT connection %p) Failed to create semaphore for "\r
+ "waitable operation.",\r
+ pMqttConnection );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );\r
+ }\r
+ else\r
+ {\r
+ /* A waitable operation is created with an additional reference for the\r
+ * Wait function. */\r
+ ( pOperation->u.operation.jobReference )++;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* If the waitable flag isn't set but a callback is, copy the callback\r
+ * information. */\r
+ if( pCallbackInfo != NULL )\r
+ {\r
+ pOperation->u.operation.notify.callback = *pCallbackInfo;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ /* Add this operation to the MQTT connection's operation list. */\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+ IotListDouble_InsertHead( &( pMqttConnection->pendingProcessing ),\r
+ &( pOperation->link ) );\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* Set the output parameter. */\r
+ *pNewOperation = pOperation;\r
+\r
+ /* Clean up operation and decrement reference count if this function failed. */\r
+ IOT_FUNCTION_CLEANUP_BEGIN();\r
+\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ if( decrementOnError == true )\r
+ {\r
+ _IotMqtt_DecrementConnectionReferences( pMqttConnection );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pOperation != NULL )\r
+ {\r
+ IotMqtt_FreeOperation( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_CLEANUP_END();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+bool _IotMqtt_DecrementOperationReferences( _mqttOperation_t * pOperation,\r
+ bool cancelJob )\r
+{\r
+ bool destroyOperation = false;\r
+ IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;\r
+ _mqttConnection_t * pMqttConnection = pOperation->pMqttConnection;\r
+\r
+ /* Attempt to cancel the operation's job. */\r
+ if( cancelJob == true )\r
+ {\r
+ taskPoolStatus = IotTaskPool_TryCancel( IOT_SYSTEM_TASKPOOL,\r
+ pOperation->job,\r
+ NULL );\r
+\r
+ /* If the operation's job was not canceled, it must be already executing.\r
+ * Any other return value is invalid. */\r
+ IotMqtt_Assert( ( taskPoolStatus == IOT_TASKPOOL_SUCCESS ) ||\r
+ ( taskPoolStatus == IOT_TASKPOOL_CANCEL_FAILED ) );\r
+\r
+ if( taskPoolStatus == IOT_TASKPOOL_SUCCESS )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Job canceled.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Decrement job reference count. */\r
+ if( taskPoolStatus == IOT_TASKPOOL_SUCCESS )\r
+ {\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+ pOperation->u.operation.jobReference--;\r
+\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Job reference changed"\r
+ " from %ld to %ld.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation,\r
+ pOperation->u.operation.jobReference + 1,\r
+ pOperation->u.operation.jobReference );\r
+\r
+ /* The job reference count must be 0 or 1 after the decrement. */\r
+ IotMqtt_Assert( ( pOperation->u.operation.jobReference == 0 ) ||\r
+ ( pOperation->u.operation.jobReference == 1 ) );\r
+\r
+ /* This operation may be destroyed if its reference count is 0. */\r
+ if( pOperation->u.operation.jobReference == 0 )\r
+ {\r
+ destroyOperation = true;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return destroyOperation;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_DestroyOperation( _mqttOperation_t * pOperation )\r
+{\r
+ _mqttConnection_t * pMqttConnection = pOperation->pMqttConnection;\r
+\r
+ /* Default free packet function. */\r
+ void ( * freePacket )( uint8_t * ) = _IotMqtt_FreePacket;\r
+\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Destroying operation.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+\r
+ /* The job reference count must be between 0 and 2. */\r
+ IotMqtt_Assert( ( pOperation->u.operation.jobReference >= 0 ) &&\r
+ ( pOperation->u.operation.jobReference <= 2 ) );\r
+\r
+ /* Jobs to be destroyed should be removed from the MQTT connection's\r
+ * lists. */\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ if( IotLink_IsLinked( &( pOperation->link ) ) == true )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Removed operation from connection lists.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation,\r
+ pMqttConnection );\r
+\r
+ IotListDouble_Remove( &( pOperation->link ) );\r
+ }\r
+ else\r
+ {\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Operation was not present in connection lists.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* Free any allocated MQTT packet. */\r
+ if( pOperation->u.operation.pMqttPacket != NULL )\r
+ {\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ if( pMqttConnection->pSerializer != NULL )\r
+ {\r
+ if( pMqttConnection->pSerializer->freePacket != NULL )\r
+ {\r
+ freePacket = pMqttConnection->pSerializer->freePacket;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ #endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */\r
+\r
+ freePacket( pOperation->u.operation.pMqttPacket );\r
+\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) MQTT packet freed.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+ }\r
+ else\r
+ {\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Operation has no allocated MQTT packet.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+ }\r
+\r
+ /* Check if a wait semaphore was created for this operation. */\r
+ if( ( pOperation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE ) == IOT_MQTT_FLAG_WAITABLE )\r
+ {\r
+ IotSemaphore_Destroy( &( pOperation->u.operation.notify.waitSemaphore ) );\r
+\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Wait semaphore destroyed.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Operation record destroyed.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+\r
+ /* Free the memory used to hold operation data. */\r
+ IotMqtt_FreeOperation( pOperation );\r
+\r
+ /* Decrement the MQTT connection's reference count after destroying an\r
+ * operation. */\r
+ _IotMqtt_DecrementConnectionReferences( pMqttConnection );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_ProcessKeepAlive( IotTaskPool_t pTaskPool,\r
+ IotTaskPoolJob_t pKeepAliveJob,\r
+ void * pContext )\r
+{\r
+ bool status = true;\r
+ IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;\r
+ size_t bytesSent = 0;\r
+\r
+ /* Retrieve the MQTT connection from the context. */\r
+ _mqttConnection_t * pMqttConnection = ( _mqttConnection_t * ) pContext;\r
+\r
+ /* Check parameters. */\r
+ IotMqtt_Assert( pTaskPool == IOT_SYSTEM_TASKPOOL );\r
+ IotMqtt_Assert( pKeepAliveJob == pMqttConnection->keepAliveJob );\r
+\r
+ /* Check that keep-alive interval is valid. The MQTT spec states its maximum\r
+ * value is 65,535 seconds. */\r
+ IotMqtt_Assert( pMqttConnection->keepAliveMs <= 65535000 );\r
+\r
+ /* Only two values are valid for the next keep alive job delay. */\r
+ IotMqtt_Assert( ( pMqttConnection->nextKeepAliveMs == pMqttConnection->keepAliveMs ) ||\r
+ ( pMqttConnection->nextKeepAliveMs == IOT_MQTT_RESPONSE_WAIT_MS ) );\r
+\r
+ IotLogDebug( "(MQTT connection %p) Keep-alive job started.", pMqttConnection );\r
+\r
+ /* Re-create the keep-alive job for rescheduling. This should never fail. */\r
+ taskPoolStatus = IotTaskPool_CreateJob( _IotMqtt_ProcessKeepAlive,\r
+ pContext,\r
+ IotTaskPool_GetJobStorageFromHandle( pKeepAliveJob ),\r
+ &pKeepAliveJob );\r
+ IotMqtt_Assert( taskPoolStatus == IOT_TASKPOOL_SUCCESS );\r
+\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* Determine whether to send a PINGREQ or check for PINGRESP. */\r
+ if( pMqttConnection->nextKeepAliveMs == pMqttConnection->keepAliveMs )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) Sending PINGREQ.", pMqttConnection );\r
+\r
+ /* Because PINGREQ may be used to keep the MQTT connection alive, it is\r
+ * more important than other operations. Bypass the queue of jobs for\r
+ * operations by directly sending the PINGREQ in this job. */\r
+ bytesSent = pMqttConnection->pNetworkInterface->send( pMqttConnection->pNetworkConnection,\r
+ pMqttConnection->pPingreqPacket,\r
+ pMqttConnection->pingreqPacketSize );\r
+\r
+ if( bytesSent != pMqttConnection->pingreqPacketSize )\r
+ {\r
+ IotLogError( "(MQTT connection %p) Failed to send PINGREQ.", pMqttConnection );\r
+ status = false;\r
+ }\r
+ else\r
+ {\r
+ /* Assume the keep-alive will fail. The network receive callback will\r
+ * clear the failure flag upon receiving a PINGRESP. */\r
+ pMqttConnection->keepAliveFailure = true;\r
+\r
+ /* Schedule a check for PINGRESP. */\r
+ pMqttConnection->nextKeepAliveMs = IOT_MQTT_RESPONSE_WAIT_MS;\r
+\r
+ IotLogDebug( "(MQTT connection %p) PINGREQ sent. Scheduling check for PINGRESP in %d ms.",\r
+ pMqttConnection,\r
+ IOT_MQTT_RESPONSE_WAIT_MS );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) Checking for PINGRESP.", pMqttConnection );\r
+\r
+ if( pMqttConnection->keepAliveFailure == false )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) PINGRESP was received.", pMqttConnection );\r
+\r
+ /* PINGRESP was received. Schedule the next PINGREQ transmission. */\r
+ pMqttConnection->nextKeepAliveMs = pMqttConnection->keepAliveMs;\r
+ }\r
+ else\r
+ {\r
+ IotLogError( "(MQTT connection %p) Failed to receive PINGRESP within %d ms.",\r
+ pMqttConnection,\r
+ IOT_MQTT_RESPONSE_WAIT_MS );\r
+\r
+ /* The network receive callback did not clear the failure flag. */\r
+ status = false;\r
+ }\r
+ }\r
+\r
+ /* When a PINGREQ is successfully sent, reschedule this job to check for a\r
+ * response shortly. */\r
+ if( status == true )\r
+ {\r
+ taskPoolStatus = IotTaskPool_ScheduleDeferred( pTaskPool,\r
+ pKeepAliveJob,\r
+ pMqttConnection->nextKeepAliveMs );\r
+\r
+ if( taskPoolStatus == IOT_TASKPOOL_SUCCESS )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) Next keep-alive job in %d ms.",\r
+ pMqttConnection,\r
+ IOT_MQTT_RESPONSE_WAIT_MS );\r
+ }\r
+ else\r
+ {\r
+ IotLogError( "(MQTT connection %p) Failed to reschedule keep-alive job, error %s.",\r
+ pMqttConnection,\r
+ IotTaskPool_strerror( taskPoolStatus ) );\r
+\r
+ status = false;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Close the connection on failures. */\r
+ if( status == false )\r
+ {\r
+ _IotMqtt_CloseNetworkConnection( IOT_MQTT_KEEP_ALIVE_TIMEOUT,\r
+ pMqttConnection );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_ProcessIncomingPublish( IotTaskPool_t pTaskPool,\r
+ IotTaskPoolJob_t pPublishJob,\r
+ void * pContext )\r
+{\r
+ _mqttOperation_t * pOperation = pContext;\r
+ IotMqttCallbackParam_t callbackParam = { .mqttConnection = NULL };\r
+\r
+ /* Check parameters. The task pool and job parameter is not used when asserts\r
+ * are disabled. */\r
+ ( void ) pTaskPool;\r
+ ( void ) pPublishJob;\r
+ IotMqtt_Assert( pTaskPool == IOT_SYSTEM_TASKPOOL );\r
+ IotMqtt_Assert( pOperation->incomingPublish == true );\r
+ IotMqtt_Assert( pPublishJob == pOperation->job );\r
+\r
+ /* Remove the operation from the pending processing list. */\r
+ IotMutex_Lock( &( pOperation->pMqttConnection->referencesMutex ) );\r
+\r
+ if( IotLink_IsLinked( &( pOperation->link ) ) == true )\r
+ {\r
+ IotListDouble_Remove( &( pOperation->link ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotMutex_Unlock( &( pOperation->pMqttConnection->referencesMutex ) );\r
+\r
+ /* Process the current PUBLISH. */\r
+ callbackParam.u.message.info = pOperation->u.publish.publishInfo;\r
+\r
+ _IotMqtt_InvokeSubscriptionCallback( pOperation->pMqttConnection,\r
+ &callbackParam );\r
+\r
+ /* Free any buffers associated with the current PUBLISH message. */\r
+ if( pOperation->u.publish.pReceivedData != NULL )\r
+ {\r
+ IotMqtt_FreeMessage( ( void * ) pOperation->u.publish.pReceivedData );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Free the incoming PUBLISH operation. */\r
+ IotMqtt_FreeOperation( pOperation );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_ProcessSend( IotTaskPool_t pTaskPool,\r
+ IotTaskPoolJob_t pSendJob,\r
+ void * pContext )\r
+{\r
+ size_t bytesSent = 0;\r
+ bool destroyOperation = false, waitable = false, networkPending = false;\r
+ _mqttOperation_t * pOperation = ( _mqttOperation_t * ) pContext;\r
+ _mqttConnection_t * pMqttConnection = pOperation->pMqttConnection;\r
+\r
+ /* Check parameters. The task pool and job parameter is not used when asserts\r
+ * are disabled. */\r
+ ( void ) pTaskPool;\r
+ ( void ) pSendJob;\r
+ IotMqtt_Assert( pTaskPool == IOT_SYSTEM_TASKPOOL );\r
+ IotMqtt_Assert( pSendJob == pOperation->job );\r
+\r
+ /* The given operation must have an allocated packet and be waiting for a status. */\r
+ IotMqtt_Assert( pOperation->u.operation.pMqttPacket != NULL );\r
+ IotMqtt_Assert( pOperation->u.operation.packetSize != 0 );\r
+ IotMqtt_Assert( pOperation->u.operation.status == IOT_MQTT_STATUS_PENDING );\r
+\r
+ /* Check if this operation is waitable. */\r
+ waitable = ( pOperation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE ) == IOT_MQTT_FLAG_WAITABLE;\r
+\r
+ /* Check PUBLISH retry counts and limits. */\r
+ if( pOperation->u.operation.retry.limit > 0 )\r
+ {\r
+ if( _checkRetryLimit( pOperation ) == false )\r
+ {\r
+ pOperation->u.operation.status = IOT_MQTT_RETRY_NO_RESPONSE;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Send an operation that is waiting for a response. */\r
+ if( pOperation->u.operation.status == IOT_MQTT_STATUS_PENDING )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Sending MQTT packet.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+\r
+ /* Transmit the MQTT packet from the operation over the network. */\r
+ bytesSent = pMqttConnection->pNetworkInterface->send( pMqttConnection->pNetworkConnection,\r
+ pOperation->u.operation.pMqttPacket,\r
+ pOperation->u.operation.packetSize );\r
+\r
+ /* Check transmission status. */\r
+ if( bytesSent != pOperation->u.operation.packetSize )\r
+ {\r
+ pOperation->u.operation.status = IOT_MQTT_NETWORK_ERROR;\r
+ }\r
+ else\r
+ {\r
+ /* DISCONNECT operations are considered successful upon successful\r
+ * transmission. In addition, non-waitable operations with no callback\r
+ * may also be considered successful. */\r
+ if( pOperation->u.operation.type == IOT_MQTT_DISCONNECT )\r
+ {\r
+ /* DISCONNECT operations are always waitable. */\r
+ IotMqtt_Assert( waitable == true );\r
+\r
+ pOperation->u.operation.status = IOT_MQTT_SUCCESS;\r
+ }\r
+ else if( waitable == false )\r
+ {\r
+ if( pOperation->u.operation.notify.callback.function == NULL )\r
+ {\r
+ pOperation->u.operation.status = IOT_MQTT_SUCCESS;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check if this operation requires further processing. */\r
+ if( pOperation->u.operation.status == IOT_MQTT_STATUS_PENDING )\r
+ {\r
+ /* Check if this operation should be scheduled for retransmission. */\r
+ if( pOperation->u.operation.retry.limit > 0 )\r
+ {\r
+ if( _scheduleNextRetry( pOperation ) == false )\r
+ {\r
+ pOperation->u.operation.status = IOT_MQTT_SCHEDULING_ERROR;\r
+ }\r
+ else\r
+ {\r
+ /* A successfully scheduled PUBLISH retry is awaiting a response\r
+ * from the network. */\r
+ networkPending = true;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* Decrement reference count to signal completion of send job. Check\r
+ * if the operation should be destroyed. */\r
+ if( waitable == true )\r
+ {\r
+ destroyOperation = _IotMqtt_DecrementOperationReferences( pOperation, false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* If the operation should not be destroyed, transfer it from the\r
+ * pending processing to the pending response list. */\r
+ if( destroyOperation == false )\r
+ {\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* Operation must be linked. */\r
+ IotMqtt_Assert( IotLink_IsLinked( &( pOperation->link ) ) );\r
+\r
+ /* Transfer to pending response list. */\r
+ IotListDouble_Remove( &( pOperation->link ) );\r
+ IotListDouble_InsertHead( &( pMqttConnection->pendingResponse ),\r
+ &( pOperation->link ) );\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ /* This operation is now awaiting a response from the network. */\r
+ networkPending = true;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Destroy the operation or notify of completion if necessary. */\r
+ if( destroyOperation == true )\r
+ {\r
+ _IotMqtt_DestroyOperation( pOperation );\r
+ }\r
+ else\r
+ {\r
+ /* Do not check the operation status if a network response is pending,\r
+ * since a network response could modify the status. */\r
+ if( networkPending == false )\r
+ {\r
+ /* Notify of operation completion if this job set a status. */\r
+ if( pOperation->u.operation.status != IOT_MQTT_STATUS_PENDING )\r
+ {\r
+ _IotMqtt_Notify( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_ProcessCompletedOperation( IotTaskPool_t pTaskPool,\r
+ IotTaskPoolJob_t pOperationJob,\r
+ void * pContext )\r
+{\r
+ _mqttOperation_t * pOperation = ( _mqttOperation_t * ) pContext;\r
+ IotMqttCallbackParam_t callbackParam = { 0 };\r
+\r
+ /* Check parameters. The task pool and job parameter is not used when asserts\r
+ * are disabled. */\r
+ ( void ) pTaskPool;\r
+ ( void ) pOperationJob;\r
+ IotMqtt_Assert( pTaskPool == IOT_SYSTEM_TASKPOOL );\r
+ IotMqtt_Assert( pOperationJob == pOperation->job );\r
+\r
+ /* The operation's callback function and status must be set. */\r
+ IotMqtt_Assert( pOperation->u.operation.notify.callback.function != NULL );\r
+ IotMqtt_Assert( pOperation->u.operation.status != IOT_MQTT_STATUS_PENDING );\r
+\r
+ callbackParam.mqttConnection = pOperation->pMqttConnection;\r
+ callbackParam.u.operation.type = pOperation->u.operation.type;\r
+ callbackParam.u.operation.reference = pOperation;\r
+ callbackParam.u.operation.result = pOperation->u.operation.status;\r
+\r
+ /* Invoke the user callback function. */\r
+ pOperation->u.operation.notify.callback.function( pOperation->u.operation.notify.callback.pCallbackContext,\r
+ &callbackParam );\r
+\r
+ /* Attempt to destroy the operation once the user callback returns. */\r
+ if( _IotMqtt_DecrementOperationReferences( pOperation, false ) == true )\r
+ {\r
+ _IotMqtt_DestroyOperation( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_ScheduleOperation( _mqttOperation_t * pOperation,\r
+ IotTaskPoolRoutine_t jobRoutine,\r
+ uint32_t delay )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_SUCCESS;\r
+ IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;\r
+\r
+ /* Check that job routine is valid. */\r
+ IotMqtt_Assert( ( jobRoutine == _IotMqtt_ProcessSend ) ||\r
+ ( jobRoutine == _IotMqtt_ProcessCompletedOperation ) ||\r
+ ( jobRoutine == _IotMqtt_ProcessIncomingPublish ) );\r
+\r
+ /* Creating a new job should never fail when parameters are valid. */\r
+ taskPoolStatus = IotTaskPool_CreateJob( jobRoutine,\r
+ pOperation,\r
+ &( pOperation->jobStorage ),\r
+ &( pOperation->job ) );\r
+ IotMqtt_Assert( taskPoolStatus == IOT_TASKPOOL_SUCCESS );\r
+\r
+ /* Schedule the new job with a delay. */\r
+ taskPoolStatus = IotTaskPool_ScheduleDeferred( IOT_SYSTEM_TASKPOOL,\r
+ pOperation->job,\r
+ delay );\r
+\r
+ if( taskPoolStatus != IOT_TASKPOOL_SUCCESS )\r
+ {\r
+ /* Scheduling a newly-created job should never be invalid or illegal. */\r
+ IotMqtt_Assert( taskPoolStatus != IOT_TASKPOOL_BAD_PARAMETER );\r
+ IotMqtt_Assert( taskPoolStatus != IOT_TASKPOOL_ILLEGAL_OPERATION );\r
+\r
+ IotLogWarn( "(MQTT connection %p, %s operation %p) Failed to schedule operation job, error %s.",\r
+ pOperation->pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation,\r
+ IotTaskPool_strerror( taskPoolStatus ) );\r
+\r
+ status = IOT_MQTT_SCHEDULING_ERROR;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+_mqttOperation_t * _IotMqtt_FindOperation( _mqttConnection_t * pMqttConnection,\r
+ IotMqttOperationType_t type,\r
+ const uint16_t * pPacketIdentifier )\r
+{\r
+ bool waitable = false;\r
+ IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;\r
+ _mqttOperation_t * pResult = NULL;\r
+ IotLink_t * pResultLink = NULL;\r
+ _operationMatchParam_t param = { .type = type, .pPacketIdentifier = pPacketIdentifier };\r
+\r
+ if( pPacketIdentifier != NULL )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) Searching for operation %s pending response "\r
+ "with packet identifier %hu.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( type ),\r
+ *pPacketIdentifier );\r
+ }\r
+ else\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) Searching for operation %s pending response.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( type ) );\r
+ }\r
+\r
+ /* Find and remove the first matching element in the list. */\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+ pResultLink = IotListDouble_FindFirstMatch( &( pMqttConnection->pendingResponse ),\r
+ NULL,\r
+ _mqttOperation_match,\r
+ ¶m );\r
+\r
+ /* Check if a match was found. */\r
+ if( pResultLink != NULL )\r
+ {\r
+ /* Get operation pointer and check if it is waitable. */\r
+ pResult = IotLink_Container( _mqttOperation_t, pResultLink, link );\r
+ waitable = ( pResult->u.operation.flags & IOT_MQTT_FLAG_WAITABLE ) == IOT_MQTT_FLAG_WAITABLE;\r
+\r
+ /* Check if the matched operation is a PUBLISH with retry. If it is, cancel\r
+ * the retry job. */\r
+ if( pResult->u.operation.retry.limit > 0 )\r
+ {\r
+ taskPoolStatus = IotTaskPool_TryCancel( IOT_SYSTEM_TASKPOOL,\r
+ pResult->job,\r
+ NULL );\r
+\r
+ /* If the retry job could not be canceled, then it is currently\r
+ * executing. Ignore the operation. */\r
+ if( taskPoolStatus != IOT_TASKPOOL_SUCCESS )\r
+ {\r
+ pResult = NULL;\r
+ }\r
+ else\r
+ {\r
+ /* Check job reference counts. A waitable operation should have a\r
+ * count of 2; a non-waitable operation should have a count of 1. */\r
+ IotMqtt_Assert( pResult->u.operation.jobReference == ( 1 + ( waitable == true ) ) );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* An operation with no retry in the pending responses list should\r
+ * always have a job reference of 1. */\r
+ IotMqtt_Assert( pResult->u.operation.jobReference == 1 );\r
+\r
+ /* Increment job references of a waitable operation to prevent Wait from\r
+ * destroying this operation if it times out. */\r
+ if( waitable == true )\r
+ {\r
+ ( pResult->u.operation.jobReference )++;\r
+\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Job reference changed from %ld to %ld.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( type ),\r
+ pResult,\r
+ ( long int ) ( pResult->u.operation.jobReference - 1 ),\r
+ ( long int ) ( pResult->u.operation.jobReference ) );\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pResult != NULL )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) Found operation %s." ,\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( type ) );\r
+\r
+ /* Remove the matched operation from the list. */\r
+ IotListDouble_Remove( &( pResult->link ) );\r
+ }\r
+ else\r
+ {\r
+ IotLogDebug( "(MQTT connection %p) Operation %s not found.",\r
+ pMqttConnection,\r
+ IotMqtt_OperationType( type ) );\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ return pResult;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_Notify( _mqttOperation_t * pOperation )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_SCHEDULING_ERROR;\r
+ _mqttConnection_t * pMqttConnection = pOperation->pMqttConnection;\r
+\r
+ /* Check if operation is waitable. */\r
+ bool waitable = ( pOperation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE ) == IOT_MQTT_FLAG_WAITABLE;\r
+\r
+ /* Remove any lingering subscriptions if a SUBSCRIBE failed. Rejected\r
+ * subscriptions are removed by the deserializer, so not removed here. */\r
+ if( pOperation->u.operation.type == IOT_MQTT_SUBSCRIBE )\r
+ {\r
+ switch( pOperation->u.operation.status )\r
+ {\r
+ case IOT_MQTT_SUCCESS:\r
+ break;\r
+\r
+ case IOT_MQTT_SERVER_REFUSED:\r
+ break;\r
+\r
+ default:\r
+ _IotMqtt_RemoveSubscriptionByPacket( pOperation->pMqttConnection,\r
+ pOperation->u.operation.packetIdentifier,\r
+ -1 );\r
+ break;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Schedule callback invocation for non-waitable operation. */\r
+ if( waitable == false )\r
+ {\r
+ /* Non-waitable operation should have job reference of 1. */\r
+ IotMqtt_Assert( pOperation->u.operation.jobReference == 1 );\r
+\r
+ /* Schedule an invocation of the callback. */\r
+ if( pOperation->u.operation.notify.callback.function != NULL )\r
+ {\r
+ IotMutex_Lock( &( pMqttConnection->referencesMutex ) );\r
+\r
+ status = _IotMqtt_ScheduleOperation( pOperation,\r
+ _IotMqtt_ProcessCompletedOperation,\r
+ 0 );\r
+\r
+ if( status == IOT_MQTT_SUCCESS )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Callback scheduled.",\r
+ pOperation->pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+\r
+ /* Place the scheduled operation back in the list of operations pending\r
+ * processing. */\r
+ if( IotLink_IsLinked( &( pOperation->link ) ) == true )\r
+ {\r
+ IotListDouble_Remove( &( pOperation->link ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotListDouble_InsertHead( &( pMqttConnection->pendingProcessing ),\r
+ &( pOperation->link ) );\r
+ }\r
+ else\r
+ {\r
+ IotLogWarn( "(MQTT connection %p, %s operation %p) Failed to schedule callback.",\r
+ pOperation->pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Operations that weren't scheduled may be destroyed. */\r
+ if( status == IOT_MQTT_SCHEDULING_ERROR )\r
+ {\r
+ /* Decrement reference count of operations not scheduled. */\r
+ if( _IotMqtt_DecrementOperationReferences( pOperation, false ) == true )\r
+ {\r
+ _IotMqtt_DestroyOperation( pOperation );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Post to a waitable operation's semaphore. */\r
+ if( waitable == true )\r
+ {\r
+ IotLogDebug( "(MQTT connection %p, %s operation %p) Waitable operation "\r
+ "notified of completion.",\r
+ pOperation->pMqttConnection,\r
+ IotMqtt_OperationType( pOperation->u.operation.type ),\r
+ pOperation );\r
+\r
+ IotSemaphore_Post( &( pOperation->u.operation.notify.waitSemaphore ) );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ IotMqtt_Assert( status == IOT_MQTT_SUCCESS );\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_serialize.c\r
+ * @brief Implements functions that generate and decode MQTT network packets.\r
+ */\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Standard includes. */\r
+#include <string.h>\r
+\r
+/* Error handling include. */\r
+#include "private/iot_error.h"\r
+\r
+/* MQTT internal includes. */\r
+#include "private/iot_mqtt_internal.h"\r
+\r
+/* Platform layer includes. */\r
+#include "platform/iot_threads.h"\r
+\r
+/* Atomic operations. */\r
+#include "iot_atomic.h"\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/*\r
+ * Macros for reading the high and low byte of a 2-byte unsigned int.\r
+ */\r
+#define UINT16_HIGH_BYTE( x ) ( ( uint8_t ) ( x >> 8 ) ) /**< @brief Get high byte. */\r
+#define UINT16_LOW_BYTE( x ) ( ( uint8_t ) ( x & 0x00ff ) ) /**< @brief Get low byte. */\r
+\r
+/**\r
+ * @brief Macro for decoding a 2-byte unsigned int from a sequence of bytes.\r
+ *\r
+ * @param[in] ptr A uint8_t* that points to the high byte.\r
+ */\r
+#define UINT16_DECODE( ptr ) \\r
+ ( uint16_t ) ( ( ( ( uint16_t ) ( *( ptr ) ) ) << 8 ) | \\r
+ ( ( uint16_t ) ( *( ptr + 1 ) ) ) )\r
+\r
+/**\r
+ * @brief Macro for setting a bit in a 1-byte unsigned int.\r
+ *\r
+ * @param[in] x The unsigned int to set.\r
+ * @param[in] position Which bit to set.\r
+ */\r
+#define UINT8_SET_BIT( x, position ) ( x = ( uint8_t ) ( x | ( 0x01 << position ) ) )\r
+\r
+/**\r
+ * @brief Macro for checking if a bit is set in a 1-byte unsigned int.\r
+ *\r
+ * @param[in] x The unsigned int to check.\r
+ * @param[in] position Which bit to check.\r
+ */\r
+#define UINT8_CHECK_BIT( x, position ) ( ( x & ( 0x01 << position ) ) == ( 0x01 << position ) )\r
+\r
+/*\r
+ * Positions of each flag in the "Connect Flag" field of an MQTT CONNECT\r
+ * packet.\r
+ */\r
+#define MQTT_CONNECT_FLAG_CLEAN ( 1 ) /**< @brief Clean session. */\r
+#define MQTT_CONNECT_FLAG_WILL ( 2 ) /**< @brief Will present. */\r
+#define MQTT_CONNECT_FLAG_WILL_QOS1 ( 3 ) /**< @brief Will QoS1. */\r
+#define MQTT_CONNECT_FLAG_WILL_QOS2 ( 4 ) /**< @brief Will QoS2. */\r
+#define MQTT_CONNECT_FLAG_WILL_RETAIN ( 5 ) /**< @brief Will retain. */\r
+#define MQTT_CONNECT_FLAG_PASSWORD ( 6 ) /**< @brief Password present. */\r
+#define MQTT_CONNECT_FLAG_USERNAME ( 7 ) /**< @brief Username present. */\r
+\r
+/*\r
+ * Positions of each flag in the first byte of an MQTT PUBLISH packet's\r
+ * fixed header.\r
+ */\r
+#define MQTT_PUBLISH_FLAG_RETAIN ( 0 ) /**< @brief Message retain flag. */\r
+#define MQTT_PUBLISH_FLAG_QOS1 ( 1 ) /**< @brief Publish QoS 1. */\r
+#define MQTT_PUBLISH_FLAG_QOS2 ( 2 ) /**< @brief Publish QoS 2. */\r
+#define MQTT_PUBLISH_FLAG_DUP ( 3 ) /**< @brief Duplicate message. */\r
+\r
+/**\r
+ * @brief The constant specifying MQTT version 3.1.1. Placed in the CONNECT packet.\r
+ */\r
+#define MQTT_VERSION_3_1_1 ( ( uint8_t ) 4U )\r
+\r
+/**\r
+ * @brief Per the MQTT 3.1.1 spec, the largest "Remaining Length" of an MQTT\r
+ * packet is this value.\r
+ */\r
+#define MQTT_MAX_REMAINING_LENGTH ( 268435455UL )\r
+\r
+/**\r
+ * @brief The maximum possible size of a CONNECT packet.\r
+ *\r
+ * All strings in a CONNECT packet are constrained to 2-byte lengths, giving a\r
+ * maximum length smaller than the max "Remaining Length" constant above.\r
+ */\r
+#define MQTT_PACKET_CONNECT_MAX_SIZE ( 327700UL )\r
+\r
+/*\r
+ * Constants relating to CONNACK packets, defined by MQTT 3.1.1 spec.\r
+ */\r
+#define MQTT_PACKET_CONNACK_REMAINING_LENGTH ( ( uint8_t ) 2 ) /**< @brief A CONNACK packet always has a "Remaining length" of 2. */\r
+#define MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK ( ( uint8_t ) 0x01 ) /**< @brief The "Session Present" bit is always the lowest bit. */\r
+\r
+/*\r
+ * Constants relating to PUBLISH and PUBACK packets, defined by MQTT\r
+ * 3.1.1 spec.\r
+ */\r
+#define MQTT_PACKET_PUBACK_SIZE ( 4 ) /**< @brief A PUBACK packet is always 4 bytes in size. */\r
+#define MQTT_PACKET_PUBACK_REMAINING_LENGTH ( ( uint8_t ) 2 ) /**< @brief A PUBACK packet always has a "Remaining length" of 2. */\r
+\r
+/*\r
+ * Constants relating to SUBACK and UNSUBACK packets, defined by MQTT\r
+ * 3.1.1 spec.\r
+ */\r
+#define MQTT_PACKET_SUBACK_MINIMUM_SIZE ( 5 ) /**< @brief The size of the smallest valid SUBACK packet. */\r
+#define MQTT_PACKET_UNSUBACK_REMAINING_LENGTH ( ( uint8_t ) 2 ) /**< @brief An UNSUBACK packet always has a "Remaining length" of 2. */\r
+\r
+/*\r
+ * Constants relating to PINGREQ and PINGRESP packets, defined by MQTT 3.1.1 spec.\r
+ */\r
+#define MQTT_PACKET_PINGREQ_SIZE ( 2 ) /**< @brief A PINGREQ packet is always 2 bytes in size. */\r
+#define MQTT_PACKET_PINGRESP_REMAINING_LENGTH ( 0 ) /**< @brief A PINGRESP packet always has a "Remaining length" of 0. */\r
+\r
+/*\r
+ * Constants relating to DISCONNECT packets, defined by MQTT 3.1.1 spec.\r
+ */\r
+#define MQTT_PACKET_DISCONNECT_SIZE ( 2 ) /**< @brief A DISCONNECT packet is always 2 bytes in size. */\r
+\r
+/* Username for metrics with AWS IoT. */\r
+#if AWS_IOT_MQTT_ENABLE_METRICS == 1 || DOXYGEN == 1\r
+ #ifndef AWS_IOT_METRICS_USERNAME\r
+\r
+/**\r
+ * @brief Specify C SDK and version.\r
+ */\r
+ #define AWS_IOT_METRICS_USERNAME "?SDK=C&Version=4.0.0"\r
+\r
+/**\r
+ * @brief The length of #AWS_IOT_METRICS_USERNAME.\r
+ */\r
+ #define AWS_IOT_METRICS_USERNAME_LENGTH ( ( uint16_t ) sizeof( AWS_IOT_METRICS_USERNAME ) - 1 )\r
+ #endif /* ifndef AWS_IOT_METRICS_USERNAME */\r
+#endif /* if AWS_IOT_MQTT_ENABLE_METRICS == 1 || DOXYGEN == 1 */\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Generate and return a 2-byte packet identifier.\r
+ *\r
+ * This packet identifier will be nonzero.\r
+ *\r
+ * @return The packet identifier.\r
+ */\r
+static uint16_t _nextPacketIdentifier( void );\r
+\r
+/**\r
+ * @brief Calculate the number of bytes required to encode an MQTT\r
+ * "Remaining length" field.\r
+ *\r
+ * @param[in] length The value of the "Remaining length" to encode.\r
+ *\r
+ * @return The size of the encoding of length. This is always `1`, `2`, `3`, or `4`.\r
+ */\r
+static size_t _remainingLengthEncodedSize( size_t length );\r
+\r
+/**\r
+ * @brief Encode the "Remaining length" field per MQTT spec.\r
+ *\r
+ * @param[out] pDestination Where to write the encoded "Remaining length".\r
+ * @param[in] length The "Remaining length" to encode.\r
+ *\r
+ * @return Pointer to the end of the encoded "Remaining length", which is 1-4\r
+ * bytes greater than `pDestination`.\r
+ *\r
+ * @warning This function does not check the size of `pDestination`! Ensure that\r
+ * `pDestination` is large enough to hold the encoded "Remaining length" using\r
+ * the function #_remainingLengthEncodedSize to avoid buffer overflows.\r
+ */\r
+static uint8_t * _encodeRemainingLength( uint8_t * pDestination,\r
+ size_t length );\r
+\r
+/**\r
+ * @brief Encode a C string as a UTF-8 string, per MQTT 3.1.1 spec.\r
+ *\r
+ * @param[out] pDestination Where to write the encoded string.\r
+ * @param[in] source The string to encode.\r
+ * @param[in] sourceLength The length of source.\r
+ *\r
+ * @return Pointer to the end of the encoded string, which is `sourceLength+2`\r
+ * bytes greater than `pDestination`.\r
+ *\r
+ * @warning This function does not check the size of `pDestination`! Ensure that\r
+ * `pDestination` is large enough to hold `sourceLength+2` bytes to avoid a buffer\r
+ * overflow.\r
+ */\r
+static uint8_t * _encodeString( uint8_t * pDestination,\r
+ const char * source,\r
+ uint16_t sourceLength );\r
+\r
+/**\r
+ * @brief Calculate the size and "Remaining length" of a CONNECT packet generated\r
+ * from the given parameters.\r
+ *\r
+ * @param[in] pConnectInfo User-provided CONNECT information struct.\r
+ * @param[out] pRemainingLength Output for calculated "Remaining length" field.\r
+ * @param[out] pPacketSize Output for calculated total packet size.\r
+ *\r
+ * @return `true` if the packet is within the length allowed by MQTT 3.1.1 spec; `false`\r
+ * otherwise. If this function returns `false`, the output parameters should be ignored.\r
+ */\r
+static bool _connectPacketSize( const IotMqttConnectInfo_t * pConnectInfo,\r
+ size_t * pRemainingLength,\r
+ size_t * pPacketSize );\r
+\r
+/**\r
+ * @brief Calculate the size and "Remaining length" of a PUBLISH packet generated\r
+ * from the given parameters.\r
+ *\r
+ * @param[in] pPublishInfo User-provided PUBLISH information struct.\r
+ * @param[out] pRemainingLength Output for calculated "Remaining length" field.\r
+ * @param[out] pPacketSize Output for calculated total packet size.\r
+ *\r
+ * @return `true` if the packet is within the length allowed by MQTT 3.1.1 spec; `false`\r
+ * otherwise. If this function returns `false`, the output parameters should be ignored.\r
+ */\r
+static bool _publishPacketSize( const IotMqttPublishInfo_t * pPublishInfo,\r
+ size_t * pRemainingLength,\r
+ size_t * pPacketSize );\r
+\r
+/**\r
+ * @brief Calculate the size and "Remaining length" of a SUBSCRIBE or UNSUBSCRIBE\r
+ * packet generated from the given parameters.\r
+ *\r
+ * @param[in] type Either IOT_MQTT_SUBSCRIBE or IOT_MQTT_UNSUBSCRIBE.\r
+ * @param[in] pSubscriptionList User-provided array of subscriptions.\r
+ * @param[in] subscriptionCount Size of `pSubscriptionList`.\r
+ * @param[out] pRemainingLength Output for calculated "Remaining length" field.\r
+ * @param[out] pPacketSize Output for calculated total packet size.\r
+ *\r
+ * @return `true` if the packet is within the length allowed by MQTT 3.1.1 spec; `false`\r
+ * otherwise. If this function returns `false`, the output parameters should be ignored.\r
+ */\r
+static bool _subscriptionPacketSize( IotMqttOperationType_t type,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ size_t * pRemainingLength,\r
+ size_t * pPacketSize );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE\r
+\r
+/**\r
+ * @brief If logging is enabled, define a log configuration that only prints the log\r
+ * string. This is used when printing out details of deserialized MQTT packets.\r
+ */\r
+ static const IotLogConfig_t _logHideAll =\r
+ {\r
+ .hideLibraryName = true,\r
+ .hideLogLevel = true,\r
+ .hideTimestring = true\r
+ };\r
+#endif\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static uint16_t _nextPacketIdentifier( void )\r
+{\r
+ /* MQTT specifies 2 bytes for the packet identifier; however, operating on\r
+ * 32-bit integers is generally faster. */\r
+ static uint32_t nextPacketIdentifier = 1;\r
+\r
+ /* The next packet identifier will be greater by 2. This prevents packet\r
+ * identifiers from ever being 0, which is not allowed by MQTT 3.1.1. Packet\r
+ * identifiers will follow the sequence 1,3,5...65535,1,3,5... */\r
+ return ( uint16_t ) Atomic_Add_u32( &nextPacketIdentifier, 2 );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static size_t _remainingLengthEncodedSize( size_t length )\r
+{\r
+ size_t encodedSize = 0;\r
+\r
+ /* length should have already been checked before calling this function. */\r
+ IotMqtt_Assert( length <= MQTT_MAX_REMAINING_LENGTH );\r
+\r
+ /* Determine how many bytes are needed to encode length.\r
+ * The values below are taken from the MQTT 3.1.1 spec. */\r
+\r
+ /* 1 byte is needed to encode lengths between 0 and 127. */\r
+ if( length < 128 )\r
+ {\r
+ encodedSize = 1;\r
+ }\r
+ /* 2 bytes are needed to encode lengths between 128 and 16,383. */\r
+ else if( length < 16384 )\r
+ {\r
+ encodedSize = 2;\r
+ }\r
+ /* 3 bytes are needed to encode lengths between 16,384 and 2,097,151. */\r
+ else if( length < 2097152 )\r
+ {\r
+ encodedSize = 3;\r
+ }\r
+ /* 4 bytes are needed to encode lengths between 2,097,152 and 268,435,455. */\r
+ else\r
+ {\r
+ encodedSize = 4;\r
+ }\r
+\r
+ return encodedSize;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static uint8_t * _encodeRemainingLength( uint8_t * pDestination,\r
+ size_t length )\r
+{\r
+ uint8_t lengthByte = 0, * pLengthEnd = pDestination;\r
+\r
+ /* This algorithm is copied from the MQTT v3.1.1 spec. */\r
+ do\r
+ {\r
+ lengthByte = length % 128;\r
+ length = length / 128;\r
+\r
+ /* Set the high bit of this byte, indicating that there's more data. */\r
+ if( length > 0 )\r
+ {\r
+ UINT8_SET_BIT( lengthByte, 7 );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Output a single encoded byte. */\r
+ *pLengthEnd = lengthByte;\r
+ pLengthEnd++;\r
+ } while( length > 0 );\r
+\r
+ return pLengthEnd;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static uint8_t * _encodeString( uint8_t * pDestination,\r
+ const char * source,\r
+ uint16_t sourceLength )\r
+{\r
+ /* The first byte of a UTF-8 string is the high byte of the string length. */\r
+ *pDestination = UINT16_HIGH_BYTE( sourceLength );\r
+ pDestination++;\r
+\r
+ /* The second byte of a UTF-8 string is the low byte of the string length. */\r
+ *pDestination = UINT16_LOW_BYTE( sourceLength );\r
+ pDestination++;\r
+\r
+ /* Copy the string into pDestination. */\r
+ ( void ) memcpy( pDestination, source, sourceLength );\r
+\r
+ /* Return the pointer to the end of the encoded string. */\r
+ pDestination += sourceLength;\r
+\r
+ return pDestination;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _connectPacketSize( const IotMqttConnectInfo_t * pConnectInfo,\r
+ size_t * pRemainingLength,\r
+ size_t * pPacketSize )\r
+{\r
+ bool status = true;\r
+ size_t connectPacketSize = 0, remainingLength = 0;\r
+\r
+ /* The CONNECT packet will always include a 10-byte variable header. */\r
+ connectPacketSize += 10U;\r
+\r
+ /* Add the length of the client identifier if provided. */\r
+ if( pConnectInfo->clientIdentifierLength > 0 )\r
+ {\r
+ connectPacketSize += pConnectInfo->clientIdentifierLength + sizeof( uint16_t );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Add the lengths of the will message and topic name if provided. */\r
+ if( pConnectInfo->pWillInfo != NULL )\r
+ {\r
+ connectPacketSize += pConnectInfo->pWillInfo->topicNameLength + sizeof( uint16_t ) +\r
+ pConnectInfo->pWillInfo->payloadLength + sizeof( uint16_t );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Depending on the status of metrics, add the length of the metrics username\r
+ * or the user-provided username. */\r
+ if( pConnectInfo->awsIotMqttMode == true )\r
+ {\r
+ #if AWS_IOT_MQTT_ENABLE_METRICS == 1\r
+ connectPacketSize += AWS_IOT_METRICS_USERNAME_LENGTH + sizeof( uint16_t );\r
+ #endif\r
+ }\r
+ else\r
+ {\r
+ /* Add the lengths of the username and password if provided and not\r
+ * connecting to an AWS IoT MQTT server. */\r
+ if( pConnectInfo->pUserName != NULL )\r
+ {\r
+ connectPacketSize += pConnectInfo->userNameLength + sizeof( uint16_t );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pConnectInfo->pPassword != NULL )\r
+ {\r
+ connectPacketSize += pConnectInfo->passwordLength + sizeof( uint16_t );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ /* At this point, the "Remaining Length" field of the MQTT CONNECT packet has\r
+ * been calculated. */\r
+ remainingLength = connectPacketSize;\r
+\r
+ /* Calculate the full size of the MQTT CONNECT packet by adding the size of\r
+ * the "Remaining Length" field plus 1 byte for the "Packet Type" field. */\r
+ connectPacketSize += 1 + _remainingLengthEncodedSize( connectPacketSize );\r
+\r
+ /* Check that the CONNECT packet is within the bounds of the MQTT spec. */\r
+ if( connectPacketSize > MQTT_PACKET_CONNECT_MAX_SIZE )\r
+ {\r
+ status = false;\r
+ }\r
+ else\r
+ {\r
+ *pRemainingLength = remainingLength;\r
+ *pPacketSize = connectPacketSize;\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _publishPacketSize( const IotMqttPublishInfo_t * pPublishInfo,\r
+ size_t * pRemainingLength,\r
+ size_t * pPacketSize )\r
+{\r
+ bool status = true;\r
+ size_t publishPacketSize = 0, payloadLimit = 0;\r
+\r
+ /* The variable header of a PUBLISH packet always contains the topic name. */\r
+ publishPacketSize += pPublishInfo->topicNameLength + sizeof( uint16_t );\r
+\r
+ /* The variable header of a QoS 1 or 2 PUBLISH packet contains a 2-byte\r
+ * packet identifier. */\r
+ if( pPublishInfo->qos > IOT_MQTT_QOS_0 )\r
+ {\r
+ publishPacketSize += sizeof( uint16_t );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Calculate the maximum allowed size of the payload for the given parameters.\r
+ * This calculation excludes the "Remaining length" encoding, whose size is not\r
+ * yet known. */\r
+ payloadLimit = MQTT_MAX_REMAINING_LENGTH - publishPacketSize - 1;\r
+\r
+ /* Ensure that the given payload fits within the calculated limit. */\r
+ if( pPublishInfo->payloadLength > payloadLimit )\r
+ {\r
+ status = false;\r
+ }\r
+ else\r
+ {\r
+ /* Add the length of the PUBLISH payload. At this point, the "Remaining length"\r
+ * has been calculated. */\r
+ publishPacketSize += pPublishInfo->payloadLength;\r
+\r
+ /* Now that the "Remaining length" is known, recalculate the payload limit\r
+ * based on the size of its encoding. */\r
+ payloadLimit -= _remainingLengthEncodedSize( publishPacketSize );\r
+\r
+ /* Check that the given payload fits within the size allowed by MQTT spec. */\r
+ if( pPublishInfo->payloadLength > payloadLimit )\r
+ {\r
+ status = false;\r
+ }\r
+ else\r
+ {\r
+ /* Set the "Remaining length" output parameter and calculate the full\r
+ * size of the PUBLISH packet. */\r
+ *pRemainingLength = publishPacketSize;\r
+\r
+ publishPacketSize += 1 + _remainingLengthEncodedSize( publishPacketSize );\r
+ *pPacketSize = publishPacketSize;\r
+ }\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _subscriptionPacketSize( IotMqttOperationType_t type,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ size_t * pRemainingLength,\r
+ size_t * pPacketSize )\r
+{\r
+ bool status = true;\r
+ size_t i = 0, subscriptionPacketSize = 0;\r
+\r
+ /* Only SUBSCRIBE and UNSUBSCRIBE operations should call this function. */\r
+ IotMqtt_Assert( ( type == IOT_MQTT_SUBSCRIBE ) || ( type == IOT_MQTT_UNSUBSCRIBE ) );\r
+\r
+ /* The variable header of a subscription packet consists of a 2-byte packet\r
+ * identifier. */\r
+ subscriptionPacketSize += sizeof( uint16_t );\r
+\r
+ /* Sum the lengths of all subscription topic filters; add 1 byte for each\r
+ * subscription's QoS if type is IOT_MQTT_SUBSCRIBE. */\r
+ for( i = 0; i < subscriptionCount; i++ )\r
+ {\r
+ /* Add the length of the topic filter. */\r
+ subscriptionPacketSize += pSubscriptionList[ i ].topicFilterLength + sizeof( uint16_t );\r
+\r
+ /* Only SUBSCRIBE packets include the QoS. */\r
+ if( type == IOT_MQTT_SUBSCRIBE )\r
+ {\r
+ subscriptionPacketSize += 1;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ /* At this point, the "Remaining length" has been calculated. Return error\r
+ * if the "Remaining length" exceeds what is allowed by MQTT 3.1.1. Otherwise,\r
+ * set the output parameter.*/\r
+ if( subscriptionPacketSize > MQTT_MAX_REMAINING_LENGTH )\r
+ {\r
+ status = false;\r
+ }\r
+ else\r
+ {\r
+ *pRemainingLength = subscriptionPacketSize;\r
+\r
+ /* Calculate the full size of the subscription packet by adding the size of the\r
+ * "Remaining length" field plus 1 byte for the "Packet type" field. Set the\r
+ * pPacketSize output parameter. */\r
+ subscriptionPacketSize += 1 + _remainingLengthEncodedSize( subscriptionPacketSize );\r
+ *pPacketSize = subscriptionPacketSize;\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+uint8_t _IotMqtt_GetPacketType( void * pNetworkConnection,\r
+ const IotNetworkInterface_t * pNetworkInterface )\r
+{\r
+ uint8_t packetType = 0xff;\r
+\r
+ /* The MQTT packet type is in the first byte of the packet. */\r
+ ( void ) _IotMqtt_GetNextByte( pNetworkConnection,\r
+ pNetworkInterface,\r
+ &packetType );\r
+\r
+ return packetType;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+size_t _IotMqtt_GetRemainingLength( void * pNetworkConnection,\r
+ const IotNetworkInterface_t * pNetworkInterface )\r
+{\r
+ uint8_t encodedByte = 0;\r
+ size_t remainingLength = 0, multiplier = 1, bytesDecoded = 0, expectedSize = 0;\r
+\r
+ /* This algorithm is copied from the MQTT v3.1.1 spec. */\r
+ do\r
+ {\r
+ if( multiplier > 2097152 ) /* 128 ^ 3 */\r
+ {\r
+ remainingLength = MQTT_REMAINING_LENGTH_INVALID;\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ if( _IotMqtt_GetNextByte( pNetworkConnection,\r
+ pNetworkInterface,\r
+ &encodedByte ) == true )\r
+ {\r
+ remainingLength += ( encodedByte & 0x7F ) * multiplier;\r
+ multiplier *= 128;\r
+ bytesDecoded++;\r
+ }\r
+ else\r
+ {\r
+ remainingLength = MQTT_REMAINING_LENGTH_INVALID;\r
+ break;\r
+ }\r
+ }\r
+ } while( ( encodedByte & 0x80 ) != 0 );\r
+\r
+ /* Check that the decoded remaining length conforms to the MQTT specification. */\r
+ if( remainingLength != MQTT_REMAINING_LENGTH_INVALID )\r
+ {\r
+ expectedSize = _remainingLengthEncodedSize( remainingLength );\r
+\r
+ if( bytesDecoded != expectedSize )\r
+ {\r
+ remainingLength = MQTT_REMAINING_LENGTH_INVALID;\r
+ }\r
+ else\r
+ {\r
+ /* Valid remaining length should be at most 4 bytes. */\r
+ IotMqtt_Assert( bytesDecoded <= 4 );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return remainingLength;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_SerializeConnect( const IotMqttConnectInfo_t * pConnectInfo,\r
+ uint8_t ** pConnectPacket,\r
+ size_t * pPacketSize )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ uint8_t connectFlags = 0;\r
+ size_t remainingLength = 0, connectPacketSize = 0;\r
+ uint8_t * pBuffer = NULL;\r
+\r
+ /* Calculate the "Remaining length" field and total packet size. If it exceeds\r
+ * what is allowed in the MQTT standard, return an error. */\r
+ if( _connectPacketSize( pConnectInfo, &remainingLength, &connectPacketSize ) == false )\r
+ {\r
+ IotLogError( "Connect packet length exceeds %lu, which is the maximum"\r
+ " size allowed by MQTT 3.1.1.",\r
+ MQTT_PACKET_CONNECT_MAX_SIZE );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Total size of the connect packet should be larger than the "Remaining length"\r
+ * field. */\r
+ IotMqtt_Assert( connectPacketSize > remainingLength );\r
+\r
+ /* Allocate memory to hold the CONNECT packet. */\r
+ pBuffer = IotMqtt_MallocMessage( connectPacketSize );\r
+\r
+ /* Check that sufficient memory was allocated. */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for CONNECT packet." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Set the output parameters. The remainder of this function always succeeds. */\r
+ *pConnectPacket = pBuffer;\r
+ *pPacketSize = connectPacketSize;\r
+\r
+ /* The first byte in the CONNECT packet is the control packet type. */\r
+ *pBuffer = MQTT_PACKET_TYPE_CONNECT;\r
+ pBuffer++;\r
+\r
+ /* The remaining length of the CONNECT packet is encoded starting from the\r
+ * second byte. The remaining length does not include the length of the fixed\r
+ * header or the encoding of the remaining length. */\r
+ pBuffer = _encodeRemainingLength( pBuffer, remainingLength );\r
+\r
+ /* The string "MQTT" is placed at the beginning of the CONNECT packet's variable\r
+ * header. This string is 4 bytes long. */\r
+ pBuffer = _encodeString( pBuffer, "MQTT", 4 );\r
+\r
+ /* The MQTT protocol version is the second byte of the variable header. */\r
+ *pBuffer = MQTT_VERSION_3_1_1;\r
+ pBuffer++;\r
+\r
+ /* Set the CONNECT flags based on the given parameters. */\r
+ if( pConnectInfo->cleanSession == true )\r
+ {\r
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_CLEAN );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Username and password depend on MQTT mode. */\r
+ if( pConnectInfo->awsIotMqttMode == true )\r
+ {\r
+ /* Set the username flag for AWS IoT metrics. The AWS IoT MQTT server\r
+ * never uses a password. */\r
+ #if AWS_IOT_MQTT_ENABLE_METRICS == 1\r
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_USERNAME );\r
+ #endif\r
+ }\r
+ else\r
+ {\r
+ /* Set the flags for username and password if provided. */\r
+ if( pConnectInfo->pUserName != NULL )\r
+ {\r
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_USERNAME );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pConnectInfo->pPassword != NULL )\r
+ {\r
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_PASSWORD );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ /* Set will flag if an LWT is provided. */\r
+ if( pConnectInfo->pWillInfo != NULL )\r
+ {\r
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL );\r
+\r
+ /* Flags only need to be changed for will QoS 1 and 2. */\r
+ switch( pConnectInfo->pWillInfo->qos )\r
+ {\r
+ case IOT_MQTT_QOS_1:\r
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_QOS1 );\r
+ break;\r
+\r
+ case IOT_MQTT_QOS_2:\r
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_QOS2 );\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ if( pConnectInfo->pWillInfo->retain == true )\r
+ {\r
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_RETAIN );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ *pBuffer = connectFlags;\r
+ pBuffer++;\r
+\r
+ /* Write the 2 bytes of the keep alive interval into the CONNECT packet. */\r
+ *pBuffer = UINT16_HIGH_BYTE( pConnectInfo->keepAliveSeconds );\r
+ *( pBuffer + 1 ) = UINT16_LOW_BYTE( pConnectInfo->keepAliveSeconds );\r
+ pBuffer += 2;\r
+\r
+ /* Write the client identifier into the CONNECT packet. */\r
+ pBuffer = _encodeString( pBuffer,\r
+ pConnectInfo->pClientIdentifier,\r
+ pConnectInfo->clientIdentifierLength );\r
+\r
+ /* Write the will topic name and message into the CONNECT packet if provided. */\r
+ if( pConnectInfo->pWillInfo != NULL )\r
+ {\r
+ pBuffer = _encodeString( pBuffer,\r
+ pConnectInfo->pWillInfo->pTopicName,\r
+ pConnectInfo->pWillInfo->topicNameLength );\r
+\r
+ pBuffer = _encodeString( pBuffer,\r
+ pConnectInfo->pWillInfo->pPayload,\r
+ ( uint16_t ) pConnectInfo->pWillInfo->payloadLength );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* If metrics are enabled, write the metrics username into the CONNECT packet.\r
+ * Otherwise, write the username and password only when not connecting to an\r
+ * AWS IoT MQTT server. */\r
+ if( pConnectInfo->awsIotMqttMode == true )\r
+ {\r
+ #if AWS_IOT_MQTT_ENABLE_METRICS == 1\r
+ IotLogInfo( "Anonymous metrics (SDK language, SDK version) will be provided to AWS IoT. "\r
+ "Recompile with AWS_IOT_MQTT_ENABLE_METRICS set to 0 to disable." );\r
+\r
+ pBuffer = _encodeString( pBuffer,\r
+ AWS_IOT_METRICS_USERNAME,\r
+ AWS_IOT_METRICS_USERNAME_LENGTH );\r
+ #endif\r
+ }\r
+ else\r
+ {\r
+ if( pConnectInfo->pUserName != NULL )\r
+ {\r
+ pBuffer = _encodeString( pBuffer,\r
+ pConnectInfo->pUserName,\r
+ pConnectInfo->userNameLength );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pConnectInfo->pPassword != NULL )\r
+ {\r
+ pBuffer = _encodeString( pBuffer,\r
+ pConnectInfo->pPassword,\r
+ pConnectInfo->passwordLength );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ /* Ensure that the difference between the end and beginning of the buffer\r
+ * is equal to connectPacketSize, i.e. pBuffer did not overflow. */\r
+ IotMqtt_Assert( ( size_t ) ( pBuffer - *pConnectPacket ) == connectPacketSize );\r
+\r
+ /* Print out the serialized CONNECT packet for debugging purposes. */\r
+ IotLog_PrintBuffer( "MQTT CONNECT packet:", *pConnectPacket, connectPacketSize );\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_DeserializeConnack( _mqttPacket_t * pConnack )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ const uint8_t * pRemainingData = pConnack->pRemainingData;\r
+\r
+ /* If logging is enabled, declare the CONNACK response code strings. The\r
+ * fourth byte of CONNACK indexes into this array for the corresponding response. */\r
+ #if LIBRARY_LOG_LEVEL > IOT_LOG_NONE\r
+ static const char * pConnackResponses[ 6 ] =\r
+ {\r
+ "Connection accepted.", /* 0 */\r
+ "Connection refused: unacceptable protocol version.", /* 1 */\r
+ "Connection refused: identifier rejected.", /* 2 */\r
+ "Connection refused: server unavailable", /* 3 */\r
+ "Connection refused: bad user name or password.", /* 4 */\r
+ "Connection refused: not authorized." /* 5 */\r
+ };\r
+ #endif\r
+\r
+ /* Check that the control packet type is 0x20. */\r
+ if( pConnack->type != MQTT_PACKET_TYPE_CONNACK )\r
+ {\r
+ IotLog( IOT_LOG_ERROR,\r
+ &_logHideAll,\r
+ "Bad control packet type 0x%02x.",\r
+ pConnack->type );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* According to MQTT 3.1.1, the second byte of CONNACK must specify a\r
+ * "Remaining length" of 2. */\r
+ if( pConnack->remainingLength != MQTT_PACKET_CONNACK_REMAINING_LENGTH )\r
+ {\r
+ IotLog( IOT_LOG_ERROR,\r
+ &_logHideAll,\r
+ "CONNACK does not have remaining length of %d.",\r
+ MQTT_PACKET_CONNACK_REMAINING_LENGTH );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check the reserved bits in CONNACK. The high 7 bits of the second byte\r
+ * in CONNACK must be 0. */\r
+ if( ( pRemainingData[ 0 ] | 0x01 ) != 0x01 )\r
+ {\r
+ IotLog( IOT_LOG_ERROR,\r
+ &_logHideAll,\r
+ "Reserved bits in CONNACK incorrect." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Determine if the "Session Present" bit it set. This is the lowest bit of\r
+ * the second byte in CONNACK. */\r
+ if( ( pRemainingData[ 0 ] & MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK )\r
+ == MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK )\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "CONNACK session present bit set." );\r
+\r
+ /* MQTT 3.1.1 specifies that the fourth byte in CONNACK must be 0 if the\r
+ * "Session Present" bit is set. */\r
+ if( pRemainingData[ 1 ] != 0 )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "CONNACK session present bit not set." );\r
+ }\r
+\r
+ /* In MQTT 3.1.1, only values 0 through 5 are valid CONNACK response codes. */\r
+ if( pRemainingData[ 1 ] > 5 )\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "CONNACK response %hhu is not valid.",\r
+ pRemainingData[ 1 ] );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Print the appropriate message for the CONNACK response code if logs are\r
+ * enabled. */\r
+ #if LIBRARY_LOG_LEVEL > IOT_LOG_NONE\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "%s",\r
+ pConnackResponses[ pRemainingData[ 1 ] ] );\r
+ #endif\r
+\r
+ /* A nonzero CONNACK response code means the connection was refused. */\r
+ if( pRemainingData[ 1 ] > 0 )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_SERVER_REFUSED );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_SerializePublish( const IotMqttPublishInfo_t * pPublishInfo,\r
+ uint8_t ** pPublishPacket,\r
+ size_t * pPacketSize,\r
+ uint16_t * pPacketIdentifier,\r
+ uint8_t ** pPacketIdentifierHigh )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ uint8_t publishFlags = 0;\r
+ uint16_t packetIdentifier = 0;\r
+ size_t remainingLength = 0, publishPacketSize = 0;\r
+ uint8_t * pBuffer = NULL;\r
+\r
+ /* Calculate the "Remaining length" field and total packet size. If it exceeds\r
+ * what is allowed in the MQTT standard, return an error. */\r
+ if( _publishPacketSize( pPublishInfo, &remainingLength, &publishPacketSize ) == false )\r
+ {\r
+ IotLogError( "Publish packet remaining length exceeds %lu, which is the "\r
+ "maximum size allowed by MQTT 3.1.1.",\r
+ MQTT_MAX_REMAINING_LENGTH );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Total size of the publish packet should be larger than the "Remaining length"\r
+ * field. */\r
+ IotMqtt_Assert( publishPacketSize > remainingLength );\r
+\r
+ /* Allocate memory to hold the PUBLISH packet. */\r
+ pBuffer = IotMqtt_MallocMessage( publishPacketSize );\r
+\r
+ /* Check that sufficient memory was allocated. */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for PUBLISH packet." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Set the output parameters. The remainder of this function always succeeds. */\r
+ *pPublishPacket = pBuffer;\r
+ *pPacketSize = publishPacketSize;\r
+\r
+ /* The first byte of a PUBLISH packet contains the packet type and flags. */\r
+ publishFlags = MQTT_PACKET_TYPE_PUBLISH;\r
+\r
+ if( pPublishInfo->qos == IOT_MQTT_QOS_1 )\r
+ {\r
+ UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 );\r
+ }\r
+ else if( pPublishInfo->qos == IOT_MQTT_QOS_2 )\r
+ {\r
+ UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS2 );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pPublishInfo->retain == true )\r
+ {\r
+ UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_RETAIN );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ *pBuffer = publishFlags;\r
+ pBuffer++;\r
+\r
+ /* The "Remaining length" is encoded from the second byte. */\r
+ pBuffer = _encodeRemainingLength( pBuffer, remainingLength );\r
+\r
+ /* The topic name is placed after the "Remaining length". */\r
+ pBuffer = _encodeString( pBuffer,\r
+ pPublishInfo->pTopicName,\r
+ pPublishInfo->topicNameLength );\r
+\r
+ /* A packet identifier is required for QoS 1 and 2 messages. */\r
+ if( pPublishInfo->qos > IOT_MQTT_QOS_0 )\r
+ {\r
+ /* Get the next packet identifier. It should always be nonzero. */\r
+ packetIdentifier = _nextPacketIdentifier();\r
+ IotMqtt_Assert( packetIdentifier != 0 );\r
+\r
+ /* Set the packet identifier output parameters. */\r
+ *pPacketIdentifier = packetIdentifier;\r
+\r
+ if( pPacketIdentifierHigh != NULL )\r
+ {\r
+ *pPacketIdentifierHigh = pBuffer;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Place the packet identifier into the PUBLISH packet. */\r
+ *pBuffer = UINT16_HIGH_BYTE( packetIdentifier );\r
+ *( pBuffer + 1 ) = UINT16_LOW_BYTE( packetIdentifier );\r
+ pBuffer += 2;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* The payload is placed after the packet identifier. */\r
+ if( pPublishInfo->payloadLength > 0 )\r
+ {\r
+ ( void ) memcpy( pBuffer, pPublishInfo->pPayload, pPublishInfo->payloadLength );\r
+ pBuffer += pPublishInfo->payloadLength;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Ensure that the difference between the end and beginning of the buffer\r
+ * is equal to publishPacketSize, i.e. pBuffer did not overflow. */\r
+ IotMqtt_Assert( ( size_t ) ( pBuffer - *pPublishPacket ) == publishPacketSize );\r
+\r
+ /* Print out the serialized PUBLISH packet for debugging purposes. */\r
+ IotLog_PrintBuffer( "MQTT PUBLISH packet:", *pPublishPacket, publishPacketSize );\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_PublishSetDup( uint8_t * pPublishPacket,\r
+ uint8_t * pPacketIdentifierHigh,\r
+ uint16_t * pNewPacketIdentifier )\r
+{\r
+ uint16_t newPacketIdentifier = 0;\r
+\r
+ /* For an AWS IoT MQTT server, change the packet identifier. */\r
+ if( pPacketIdentifierHigh != NULL )\r
+ {\r
+ /* Output parameter for new packet identifier must be provided. */\r
+ IotMqtt_Assert( pNewPacketIdentifier != NULL );\r
+\r
+ /* Generate a new packet identifier. */\r
+ newPacketIdentifier = _nextPacketIdentifier();\r
+\r
+ IotLogDebug( "Changing PUBLISH packet identifier %hu to %hu.",\r
+ UINT16_DECODE( pPacketIdentifierHigh ),\r
+ newPacketIdentifier );\r
+\r
+ /* Replace the packet identifier. */\r
+ *pPacketIdentifierHigh = UINT16_HIGH_BYTE( newPacketIdentifier );\r
+ *( pPacketIdentifierHigh + 1 ) = UINT16_LOW_BYTE( newPacketIdentifier );\r
+ *pNewPacketIdentifier = newPacketIdentifier;\r
+ }\r
+ else\r
+ {\r
+ /* For a compliant MQTT 3.1.1 server, set the DUP flag. */\r
+ UINT8_SET_BIT( *pPublishPacket, MQTT_PUBLISH_FLAG_DUP );\r
+\r
+ IotLogDebug( "PUBLISH DUP flag set." );\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_DeserializePublish( _mqttPacket_t * pPublish )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ IotMqttPublishInfo_t * pOutput = &( pPublish->u.pIncomingPublish->u.publish.publishInfo );\r
+ uint8_t publishFlags = 0;\r
+ const uint8_t * pVariableHeader = pPublish->pRemainingData, * pPacketIdentifierHigh = NULL;\r
+\r
+ /* The flags are the lower 4 bits of the first byte in PUBLISH. */\r
+ publishFlags = pPublish->type;\r
+\r
+ /* Parse the Retain bit. */\r
+ pOutput->retain = UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_RETAIN );\r
+\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Retain bit is %d.", pOutput->retain );\r
+\r
+ /* Check for QoS 2. */\r
+ if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS2 ) == true )\r
+ {\r
+ /* PUBLISH packet is invalid if both QoS 1 and QoS 2 bits are set. */\r
+ if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 ) == true )\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Bad QoS: 3." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ pOutput->qos = IOT_MQTT_QOS_2;\r
+ }\r
+ /* Check for QoS 1. */\r
+ else if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 ) == true )\r
+ {\r
+ pOutput->qos = IOT_MQTT_QOS_1;\r
+ }\r
+ /* If the PUBLISH isn't QoS 1 or 2, then it's QoS 0. */\r
+ else\r
+ {\r
+ pOutput->qos = IOT_MQTT_QOS_0;\r
+ }\r
+\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "QoS is %d.", pOutput->qos );\r
+\r
+ /* Parse the DUP bit. */\r
+ if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_DUP ) == true )\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "DUP is 1." );\r
+ }\r
+ else\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "DUP is 0." );\r
+ }\r
+\r
+ /* Sanity checks for "Remaining length". */\r
+ if( pOutput->qos == IOT_MQTT_QOS_0 )\r
+ {\r
+ /* A QoS 0 PUBLISH must have a remaining length of at least 3 to accommodate\r
+ * topic name length (2 bytes) and topic name (at least 1 byte). */\r
+ if( pPublish->remainingLength < 3 )\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "QoS 0 PUBLISH cannot have a remaining length less than 3." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* A QoS 1 or 2 PUBLISH must have a remaining length of at least 5 to\r
+ * accommodate a packet identifier as well as the topic name length and\r
+ * topic name. */\r
+ if( pPublish->remainingLength < 5 )\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "QoS 1 or 2 PUBLISH cannot have a remaining length less than 5." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ /* Extract the topic name starting from the first byte of the variable header.\r
+ * The topic name string starts at byte 3 in the variable header. */\r
+ pOutput->topicNameLength = UINT16_DECODE( pVariableHeader );\r
+\r
+ /* Sanity checks for topic name length and "Remaining length". */\r
+ if( pOutput->qos == IOT_MQTT_QOS_0 )\r
+ {\r
+ /* Check that the "Remaining length" is at least as large as the variable\r
+ * header. */\r
+ if( pPublish->remainingLength < pOutput->topicNameLength + sizeof( uint16_t ) )\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Remaining length cannot be less than variable header length." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* Check that the "Remaining length" is at least as large as the variable\r
+ * header. */\r
+ if( pPublish->remainingLength < pOutput->topicNameLength + 2 * sizeof( uint16_t ) )\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Remaining length cannot be less than variable header length." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ /* Parse the topic. */\r
+ pOutput->pTopicName = ( const char * ) ( pVariableHeader + sizeof( uint16_t ) );\r
+\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Topic name length %hu: %.*s",\r
+ pOutput->topicNameLength,\r
+ pOutput->topicNameLength,\r
+ pOutput->pTopicName );\r
+\r
+ /* Extract the packet identifier for QoS 1 or 2 PUBLISH packets. Packet\r
+ * identifier starts immediately after the topic name. */\r
+ pPacketIdentifierHigh = ( const uint8_t * ) ( pOutput->pTopicName + pOutput->topicNameLength );\r
+\r
+ if( pOutput->qos > IOT_MQTT_QOS_0 )\r
+ {\r
+ pPublish->packetIdentifier = UINT16_DECODE( pPacketIdentifierHigh );\r
+\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Packet identifier %hu.", pPublish->packetIdentifier );\r
+\r
+ /* Packet identifier cannot be 0. */\r
+ if( pPublish->packetIdentifier == 0 )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Calculate the length of the payload. QoS 1 or 2 PUBLISH packets contain\r
+ * a packet identifer, but QoS 0 PUBLISH packets do not. */\r
+ if( pOutput->qos == IOT_MQTT_QOS_0 )\r
+ {\r
+ pOutput->payloadLength = ( uint16_t ) ( pPublish->remainingLength - pOutput->topicNameLength - sizeof( uint16_t ) );\r
+ pOutput->pPayload = pPacketIdentifierHigh;\r
+ }\r
+ else\r
+ {\r
+ pOutput->payloadLength = ( uint16_t ) ( pPublish->remainingLength - pOutput->topicNameLength - 2 * sizeof( uint16_t ) );\r
+ pOutput->pPayload = pPacketIdentifierHigh + sizeof( uint16_t );\r
+ }\r
+\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Payload length %hu.", pOutput->payloadLength );\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_SerializePuback( uint16_t packetIdentifier,\r
+ uint8_t ** pPubackPacket,\r
+ size_t * pPacketSize )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_SUCCESS;\r
+\r
+ /* Allocate memory for PUBACK. */\r
+ uint8_t * pBuffer = IotMqtt_MallocMessage( MQTT_PACKET_PUBACK_SIZE );\r
+\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for PUBACK packet" );\r
+\r
+ status = IOT_MQTT_NO_MEMORY;\r
+ }\r
+ else\r
+ {\r
+ /* Set the output parameters. The remainder of this function always succeeds. */\r
+ *pPubackPacket = pBuffer;\r
+ *pPacketSize = MQTT_PACKET_PUBACK_SIZE;\r
+\r
+ /* Set the 4 bytes in PUBACK. */\r
+ pBuffer[ 0 ] = MQTT_PACKET_TYPE_PUBACK;\r
+ pBuffer[ 1 ] = MQTT_PACKET_PUBACK_REMAINING_LENGTH;\r
+ pBuffer[ 2 ] = UINT16_HIGH_BYTE( packetIdentifier );\r
+ pBuffer[ 3 ] = UINT16_LOW_BYTE( packetIdentifier );\r
+\r
+ /* Print out the serialized PUBACK packet for debugging purposes. */\r
+ IotLog_PrintBuffer( "MQTT PUBACK packet:", *pPubackPacket, MQTT_PACKET_PUBACK_SIZE );\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_DeserializePuback( _mqttPacket_t * pPuback )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+\r
+ /* Check the "Remaining length" of the received PUBACK. */\r
+ if( pPuback->remainingLength != MQTT_PACKET_PUBACK_REMAINING_LENGTH )\r
+ {\r
+ IotLog( IOT_LOG_ERROR,\r
+ &_logHideAll,\r
+ "PUBACK does not have remaining length of %d.",\r
+ MQTT_PACKET_PUBACK_REMAINING_LENGTH );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Extract the packet identifier (third and fourth bytes) from PUBACK. */\r
+ pPuback->packetIdentifier = UINT16_DECODE( pPuback->pRemainingData );\r
+\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Packet identifier %hu.", pPuback->packetIdentifier );\r
+\r
+ /* Packet identifier cannot be 0. */\r
+ if( pPuback->packetIdentifier == 0 )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check that the control packet type is 0x40 (this must be done after the\r
+ * packet identifier is parsed). */\r
+ if( pPuback->type != MQTT_PACKET_TYPE_PUBACK )\r
+ {\r
+ IotLog( IOT_LOG_ERROR,\r
+ &_logHideAll,\r
+ "Bad control packet type 0x%02x.",\r
+ pPuback->type );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_SerializeSubscribe( const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint8_t ** pSubscribePacket,\r
+ size_t * pPacketSize,\r
+ uint16_t * pPacketIdentifier )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ size_t i = 0, subscribePacketSize = 0, remainingLength = 0;\r
+ uint16_t packetIdentifier = 0;\r
+ uint8_t * pBuffer = NULL;\r
+\r
+ /* Calculate the "Remaining length" field and total packet size. If it exceeds\r
+ * what is allowed in the MQTT standard, return an error. */\r
+ if( _subscriptionPacketSize( IOT_MQTT_SUBSCRIBE,\r
+ pSubscriptionList,\r
+ subscriptionCount,\r
+ &remainingLength,\r
+ &subscribePacketSize ) == false )\r
+ {\r
+ IotLogError( "Subscribe packet remaining length exceeds %lu, which is the "\r
+ "maximum size allowed by MQTT 3.1.1.",\r
+ MQTT_MAX_REMAINING_LENGTH );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Total size of the subscribe packet should be larger than the "Remaining length"\r
+ * field. */\r
+ IotMqtt_Assert( subscribePacketSize > remainingLength );\r
+\r
+ /* Allocate memory to hold the SUBSCRIBE packet. */\r
+ pBuffer = IotMqtt_MallocMessage( subscribePacketSize );\r
+\r
+ /* Check that sufficient memory was allocated. */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for SUBSCRIBE packet." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Set the output parameters. The remainder of this function always succeeds. */\r
+ *pSubscribePacket = pBuffer;\r
+ *pPacketSize = subscribePacketSize;\r
+\r
+ /* The first byte in SUBSCRIBE is the packet type. */\r
+ *pBuffer = MQTT_PACKET_TYPE_SUBSCRIBE;\r
+ pBuffer++;\r
+\r
+ /* Encode the "Remaining length" starting from the second byte. */\r
+ pBuffer = _encodeRemainingLength( pBuffer, remainingLength );\r
+\r
+ /* Get the next packet identifier. It should always be nonzero. */\r
+ packetIdentifier = _nextPacketIdentifier();\r
+ *pPacketIdentifier = packetIdentifier;\r
+ IotMqtt_Assert( packetIdentifier != 0 );\r
+\r
+ /* Place the packet identifier into the SUBSCRIBE packet. */\r
+ *pBuffer = UINT16_HIGH_BYTE( packetIdentifier );\r
+ *( pBuffer + 1 ) = UINT16_LOW_BYTE( packetIdentifier );\r
+ pBuffer += 2;\r
+\r
+ /* Serialize each subscription topic filter and QoS. */\r
+ for( i = 0; i < subscriptionCount; i++ )\r
+ {\r
+ pBuffer = _encodeString( pBuffer,\r
+ pSubscriptionList[ i ].pTopicFilter,\r
+ pSubscriptionList[ i ].topicFilterLength );\r
+\r
+ /* Place the QoS in the SUBSCRIBE packet. */\r
+ *pBuffer = ( uint8_t ) ( pSubscriptionList[ i ].qos );\r
+ pBuffer++;\r
+ }\r
+\r
+ /* Ensure that the difference between the end and beginning of the buffer\r
+ * is equal to subscribePacketSize, i.e. pBuffer did not overflow. */\r
+ IotMqtt_Assert( ( size_t ) ( pBuffer - *pSubscribePacket ) == subscribePacketSize );\r
+\r
+ /* Print out the serialized SUBSCRIBE packet for debugging purposes. */\r
+ IotLog_PrintBuffer( "MQTT SUBSCRIBE packet:", *pSubscribePacket, subscribePacketSize );\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_DeserializeSuback( _mqttPacket_t * pSuback )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ size_t i = 0, remainingLength = pSuback->remainingLength;\r
+ uint8_t subscriptionStatus = 0;\r
+ const uint8_t * pVariableHeader = pSuback->pRemainingData;\r
+\r
+ /* A SUBACK must have a remaining length of at least 3 to accommodate the\r
+ * packet identifer and at least 1 return code. */\r
+ if( remainingLength < 3 )\r
+ {\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "SUBACK cannot have a remaining length less than 3." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Extract the packet identifier (first 2 bytes of variable header) from SUBACK. */\r
+ pSuback->packetIdentifier = UINT16_DECODE( pVariableHeader );\r
+\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Packet identifier %hu.", pSuback->packetIdentifier );\r
+\r
+ /* Check that the control packet type is 0x90 (this must be done after the\r
+ * packet identifier is parsed). */\r
+ if( pSuback->type != MQTT_PACKET_TYPE_SUBACK )\r
+ {\r
+ IotLog( IOT_LOG_ERROR,\r
+ &_logHideAll,\r
+ "Bad control packet type 0x%02x.",\r
+ pSuback->type );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Iterate through each status byte in the SUBACK packet. */\r
+ for( i = 0; i < remainingLength - sizeof( uint16_t ); i++ )\r
+ {\r
+ /* Read a single status byte in SUBACK. */\r
+ subscriptionStatus = *( pVariableHeader + sizeof( uint16_t ) + i );\r
+\r
+ /* MQTT 3.1.1 defines the following values as status codes. */\r
+ switch( subscriptionStatus )\r
+ {\r
+ case 0x00:\r
+ case 0x01:\r
+ case 0x02:\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Topic filter %lu accepted, max QoS %hhu.",\r
+ ( unsigned long ) i, subscriptionStatus );\r
+ break;\r
+\r
+ case 0x80:\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Topic filter %lu refused.", ( unsigned long ) i );\r
+\r
+ /* Remove a rejected subscription from the subscription manager. */\r
+ _IotMqtt_RemoveSubscriptionByPacket( pSuback->u.pMqttConnection,\r
+ pSuback->packetIdentifier,\r
+ ( int32_t ) i );\r
+\r
+ status = IOT_MQTT_SERVER_REFUSED;\r
+\r
+ break;\r
+\r
+ default:\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Bad SUBSCRIBE status %hhu.", subscriptionStatus );\r
+\r
+ status = IOT_MQTT_BAD_RESPONSE;\r
+\r
+ break;\r
+ }\r
+\r
+ /* Stop parsing the subscription statuses if a bad response was received. */\r
+ if( status == IOT_MQTT_BAD_RESPONSE )\r
+ {\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_SerializeUnsubscribe( const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint8_t ** pUnsubscribePacket,\r
+ size_t * pPacketSize,\r
+ uint16_t * pPacketIdentifier )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+ size_t i = 0, unsubscribePacketSize = 0, remainingLength = 0;\r
+ uint16_t packetIdentifier = 0;\r
+ uint8_t * pBuffer = NULL;\r
+\r
+ /* Calculate the "Remaining length" field and total packet size. If it exceeds\r
+ * what is allowed in the MQTT standard, return an error. */\r
+ if( _subscriptionPacketSize( IOT_MQTT_UNSUBSCRIBE,\r
+ pSubscriptionList,\r
+ subscriptionCount,\r
+ &remainingLength,\r
+ &unsubscribePacketSize ) == false )\r
+ {\r
+ IotLogError( "Unsubscribe packet remaining length exceeds %lu, which is the "\r
+ "maximum size allowed by MQTT 3.1.1.",\r
+ MQTT_MAX_REMAINING_LENGTH );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Total size of the unsubscribe packet should be larger than the "Remaining length"\r
+ * field. */\r
+ IotMqtt_Assert( unsubscribePacketSize > remainingLength );\r
+\r
+ /* Allocate memory to hold the UNSUBSCRIBE packet. */\r
+ pBuffer = IotMqtt_MallocMessage( unsubscribePacketSize );\r
+\r
+ /* Check that sufficient memory was allocated. */\r
+ if( pBuffer == NULL )\r
+ {\r
+ IotLogError( "Failed to allocate memory for UNSUBSCRIBE packet." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Set the output parameters. The remainder of this function always succeeds. */\r
+ *pUnsubscribePacket = pBuffer;\r
+ *pPacketSize = unsubscribePacketSize;\r
+\r
+ /* The first byte in UNSUBSCRIBE is the packet type. */\r
+ *pBuffer = MQTT_PACKET_TYPE_UNSUBSCRIBE;\r
+ pBuffer++;\r
+\r
+ /* Encode the "Remaining length" starting from the second byte. */\r
+ pBuffer = _encodeRemainingLength( pBuffer, remainingLength );\r
+\r
+ /* Get the next packet identifier. It should always be nonzero. */\r
+ packetIdentifier = _nextPacketIdentifier();\r
+ *pPacketIdentifier = packetIdentifier;\r
+ IotMqtt_Assert( packetIdentifier != 0 );\r
+\r
+ /* Place the packet identifier into the UNSUBSCRIBE packet. */\r
+ *pBuffer = UINT16_HIGH_BYTE( packetIdentifier );\r
+ *( pBuffer + 1 ) = UINT16_LOW_BYTE( packetIdentifier );\r
+ pBuffer += 2;\r
+\r
+ /* Serialize each subscription topic filter. */\r
+ for( i = 0; i < subscriptionCount; i++ )\r
+ {\r
+ pBuffer = _encodeString( pBuffer,\r
+ pSubscriptionList[ i ].pTopicFilter,\r
+ pSubscriptionList[ i ].topicFilterLength );\r
+ }\r
+\r
+ /* Ensure that the difference between the end and beginning of the buffer\r
+ * is equal to unsubscribePacketSize, i.e. pBuffer did not overflow. */\r
+ IotMqtt_Assert( ( size_t ) ( pBuffer - *pUnsubscribePacket ) == unsubscribePacketSize );\r
+\r
+ /* Print out the serialized UNSUBSCRIBE packet for debugging purposes. */\r
+ IotLog_PrintBuffer( "MQTT UNSUBSCRIBE packet:", *pUnsubscribePacket, unsubscribePacketSize );\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_DeserializeUnsuback( _mqttPacket_t * pUnsuback )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+\r
+ /* Check the "Remaining length" (second byte) of the received UNSUBACK. */\r
+ if( pUnsuback->remainingLength != MQTT_PACKET_UNSUBACK_REMAINING_LENGTH )\r
+ {\r
+ IotLog( IOT_LOG_ERROR,\r
+ &_logHideAll,\r
+ "UNSUBACK does not have remaining length of %d.",\r
+ MQTT_PACKET_UNSUBACK_REMAINING_LENGTH );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Extract the packet identifier (third and fourth bytes) from UNSUBACK. */\r
+ pUnsuback->packetIdentifier = UINT16_DECODE( pUnsuback->pRemainingData );\r
+\r
+ /* Packet identifier cannot be 0. */\r
+ if( pUnsuback->packetIdentifier == 0 )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotLog( IOT_LOG_DEBUG,\r
+ &_logHideAll,\r
+ "Packet identifier %hu.", pUnsuback->packetIdentifier );\r
+\r
+ /* Check that the control packet type is 0xb0 (this must be done after the\r
+ * packet identifier is parsed). */\r
+ if( pUnsuback->type != MQTT_PACKET_TYPE_UNSUBACK )\r
+ {\r
+ IotLog( IOT_LOG_ERROR,\r
+ &_logHideAll,\r
+ "Bad control packet type 0x%02x.",\r
+ pUnsuback->type );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_SerializePingreq( uint8_t ** pPingreqPacket,\r
+ size_t * pPacketSize )\r
+{\r
+ /* PINGREQ packets are always the same. */\r
+ static const uint8_t pPingreq[ MQTT_PACKET_PINGREQ_SIZE ] =\r
+ {\r
+ MQTT_PACKET_TYPE_PINGREQ,\r
+ 0x00\r
+ };\r
+\r
+ /* Set the output parameters. */\r
+ *pPingreqPacket = ( uint8_t * ) pPingreq;\r
+ *pPacketSize = MQTT_PACKET_PINGREQ_SIZE;\r
+\r
+ /* Print out the PINGREQ packet for debugging purposes. */\r
+ IotLog_PrintBuffer( "MQTT PINGREQ packet:", pPingreq, MQTT_PACKET_PINGREQ_SIZE );\r
+\r
+ return IOT_MQTT_SUCCESS;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_DeserializePingresp( _mqttPacket_t * pPingresp )\r
+{\r
+ IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );\r
+\r
+ /* Check that the control packet type is 0xd0. */\r
+ if( pPingresp->type != MQTT_PACKET_TYPE_PINGRESP )\r
+ {\r
+ IotLog( IOT_LOG_ERROR,\r
+ &_logHideAll,\r
+ "Bad control packet type 0x%02x.",\r
+ pPingresp->type );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check the "Remaining length" (second byte) of the received PINGRESP. */\r
+ if( pPingresp->remainingLength != MQTT_PACKET_PINGRESP_REMAINING_LENGTH )\r
+ {\r
+ IotLog( IOT_LOG_ERROR,\r
+ &_logHideAll,\r
+ "PINGRESP does not have remaining length of %d.",\r
+ MQTT_PACKET_PINGRESP_REMAINING_LENGTH );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_SerializeDisconnect( uint8_t ** pDisconnectPacket,\r
+ size_t * pPacketSize )\r
+{\r
+ /* DISCONNECT packets are always the same. */\r
+ static const uint8_t pDisconnect[ MQTT_PACKET_DISCONNECT_SIZE ] =\r
+ {\r
+ MQTT_PACKET_TYPE_DISCONNECT,\r
+ 0x00\r
+ };\r
+\r
+ /* Set the output parameters. */\r
+ *pDisconnectPacket = ( uint8_t * ) pDisconnect;\r
+ *pPacketSize = MQTT_PACKET_DISCONNECT_SIZE;\r
+\r
+ /* Print out the DISCONNECT packet for debugging purposes. */\r
+ IotLog_PrintBuffer( "MQTT DISCONNECT packet:", pDisconnect, MQTT_PACKET_DISCONNECT_SIZE );\r
+\r
+ return IOT_MQTT_SUCCESS;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_FreePacket( uint8_t * pPacket )\r
+{\r
+ uint8_t packetType = *pPacket;\r
+\r
+ /* Don't call free on DISCONNECT and PINGREQ; those are allocated from static\r
+ * memory. */\r
+ if( packetType != MQTT_PACKET_TYPE_DISCONNECT )\r
+ {\r
+ if( packetType != MQTT_PACKET_TYPE_PINGREQ )\r
+ {\r
+ IotMqtt_FreeMessage( pPacket );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_static_memory.c\r
+ * @brief Implementation of MQTT static memory functions.\r
+ */\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* This file should only be compiled if dynamic memory allocation is forbidden. */\r
+#if IOT_STATIC_MEMORY_ONLY == 1\r
+\r
+/* Standard includes. */\r
+#include <stdbool.h>\r
+#include <stddef.h>\r
+#include <string.h>\r
+\r
+/* Static memory include. */\r
+#include "private/iot_static_memory.h"\r
+\r
+/* MQTT internal include. */\r
+#include "private/iot_mqtt_internal.h"\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @cond DOXYGEN_IGNORE\r
+ * Doxygen should ignore this section.\r
+ *\r
+ * Provide default values for undefined configuration constants.\r
+ */\r
+#ifndef IOT_MQTT_CONNECTIONS\r
+ #define IOT_MQTT_CONNECTIONS ( 1 )\r
+#endif\r
+#ifndef IOT_MQTT_MAX_IN_PROGRESS_OPERATIONS\r
+ #define IOT_MQTT_MAX_IN_PROGRESS_OPERATIONS ( 10 )\r
+#endif\r
+#ifndef IOT_MQTT_SUBSCRIPTIONS\r
+ #define IOT_MQTT_SUBSCRIPTIONS ( 8 )\r
+#endif\r
+/** @endcond */\r
+\r
+/* Validate static memory configuration settings. */\r
+#if IOT_MQTT_CONNECTIONS <= 0\r
+ #error "IOT_MQTT_CONNECTIONS cannot be 0 or negative."\r
+#endif\r
+#if IOT_MQTT_MAX_IN_PROGRESS_OPERATIONS <= 0\r
+ #error "IOT_MQTT_MAX_IN_PROGRESS_OPERATIONS cannot be 0 or negative."\r
+#endif\r
+#if IOT_MQTT_SUBSCRIPTIONS <= 0\r
+ #error "IOT_MQTT_SUBSCRIPTIONS cannot be 0 or negative."\r
+#endif\r
+\r
+/**\r
+ * @brief The size of a static memory MQTT subscription.\r
+ *\r
+ * Since the pTopic member of #_mqttSubscription_t is variable-length, the constant\r
+ * #AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH is used for the length of\r
+ * #_mqttSubscription_t.pTopicFilter.\r
+ */\r
+#define MQTT_SUBSCRIPTION_SIZE ( sizeof( _mqttSubscription_t ) + AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/*\r
+ * Static memory buffers and flags, allocated and zeroed at compile-time.\r
+ */\r
+static bool _pInUseMqttConnections[ IOT_MQTT_CONNECTIONS ] = { 0 }; /**< @brief MQTT connection in-use flags. */\r
+static _mqttConnection_t _pMqttConnections[ IOT_MQTT_CONNECTIONS ] = { { 0 } }; /**< @brief MQTT connections. */\r
+\r
+static bool _pInUseMqttOperations[ IOT_MQTT_MAX_IN_PROGRESS_OPERATIONS ] = { 0 }; /**< @brief MQTT operation in-use flags. */\r
+static _mqttOperation_t _pMqttOperations[ IOT_MQTT_MAX_IN_PROGRESS_OPERATIONS ] = { { .link = { 0 } } }; /**< @brief MQTT operations. */\r
+\r
+static bool _pInUseMqttSubscriptions[ IOT_MQTT_SUBSCRIPTIONS ] = { 0 }; /**< @brief MQTT subscription in-use flags. */\r
+static char _pMqttSubscriptions[ IOT_MQTT_SUBSCRIPTIONS ][ MQTT_SUBSCRIPTION_SIZE ] = { { 0 } }; /**< @brief MQTT subscriptions. */\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void * IotMqtt_MallocConnection( size_t size )\r
+{\r
+ int32_t freeIndex = -1;\r
+ void * pNewConnection = NULL;\r
+\r
+ /* Check size argument. */\r
+ if( size == sizeof( _mqttConnection_t ) )\r
+ {\r
+ /* Find a free MQTT connection. */\r
+ freeIndex = IotStaticMemory_FindFree( _pInUseMqttConnections,\r
+ IOT_MQTT_CONNECTIONS );\r
+\r
+ if( freeIndex != -1 )\r
+ {\r
+ pNewConnection = &( _pMqttConnections[ freeIndex ] );\r
+ }\r
+ }\r
+\r
+ return pNewConnection;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void IotMqtt_FreeConnection( void * ptr )\r
+{\r
+ /* Return the in-use MQTT connection. */\r
+ IotStaticMemory_ReturnInUse( ptr,\r
+ _pMqttConnections,\r
+ _pInUseMqttConnections,\r
+ IOT_MQTT_CONNECTIONS,\r
+ sizeof( _mqttConnection_t ) );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void * IotMqtt_MallocOperation( size_t size )\r
+{\r
+ int32_t freeIndex = -1;\r
+ void * pNewOperation = NULL;\r
+\r
+ /* Check size argument. */\r
+ if( size == sizeof( _mqttOperation_t ) )\r
+ {\r
+ /* Find a free MQTT operation. */\r
+ freeIndex = IotStaticMemory_FindFree( _pInUseMqttOperations,\r
+ IOT_MQTT_MAX_IN_PROGRESS_OPERATIONS );\r
+\r
+ if( freeIndex != -1 )\r
+ {\r
+ pNewOperation = &( _pMqttOperations[ freeIndex ] );\r
+ }\r
+ }\r
+\r
+ return pNewOperation;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void IotMqtt_FreeOperation( void * ptr )\r
+{\r
+ /* Return the in-use MQTT operation. */\r
+ IotStaticMemory_ReturnInUse( ptr,\r
+ _pMqttOperations,\r
+ _pInUseMqttOperations,\r
+ IOT_MQTT_MAX_IN_PROGRESS_OPERATIONS,\r
+ sizeof( _mqttOperation_t ) );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void * IotMqtt_MallocSubscription( size_t size )\r
+{\r
+ int32_t freeIndex = -1;\r
+ void * pNewSubscription = NULL;\r
+\r
+ if( size <= MQTT_SUBSCRIPTION_SIZE )\r
+ {\r
+ /* Get the index of a free MQTT subscription. */\r
+ freeIndex = IotStaticMemory_FindFree( _pInUseMqttSubscriptions,\r
+ IOT_MQTT_SUBSCRIPTIONS );\r
+\r
+ if( freeIndex != -1 )\r
+ {\r
+ pNewSubscription = &( _pMqttSubscriptions[ freeIndex ][ 0 ] );\r
+ }\r
+ }\r
+\r
+ return pNewSubscription;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void IotMqtt_FreeSubscription( void * ptr )\r
+{\r
+ /* Return the in-use MQTT subscription. */\r
+ IotStaticMemory_ReturnInUse( ptr,\r
+ _pMqttSubscriptions,\r
+ _pInUseMqttSubscriptions,\r
+ IOT_MQTT_SUBSCRIPTIONS,\r
+ MQTT_SUBSCRIPTION_SIZE );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+#endif\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_subscription.c\r
+ * @brief Implements functions that manage subscriptions for an MQTT connection.\r
+ */\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Standard includes. */\r
+#include <stdbool.h>\r
+#include <string.h>\r
+\r
+/* Error handling include. */\r
+#include "private/iot_error.h"\r
+\r
+/* MQTT internal include. */\r
+#include "private/iot_mqtt_internal.h"\r
+\r
+/* Platform layer includes. */\r
+#include "platform/iot_threads.h"\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief First parameter to #_topicMatch.\r
+ */\r
+typedef struct _topicMatchParams\r
+{\r
+ const char * pTopicName; /**< @brief The topic name to parse. */\r
+ uint16_t topicNameLength; /**< @brief Length of #_topicMatchParams_t.pTopicName. */\r
+ bool exactMatchOnly; /**< @brief Whether to allow wildcards or require exact matches. */\r
+} _topicMatchParams_t;\r
+\r
+/**\r
+ * @brief First parameter to #_packetMatch.\r
+ */\r
+typedef struct _packetMatchParams\r
+{\r
+ uint16_t packetIdentifier; /**< Packet identifier to match. */\r
+ int32_t order; /**< Order to match. Set to `-1` to ignore. */\r
+} _packetMatchParams_t;\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Matches a topic name (from a publish) with a topic filter (from a\r
+ * subscription).\r
+ *\r
+ * @param[in] pSubscriptionLink Pointer to the link member of an #_mqttSubscription_t.\r
+ * @param[in] pMatch Pointer to a #_topicMatchParams_t.\r
+ *\r
+ * @return `true` if the arguments match the subscription topic filter; `false`\r
+ * otherwise.\r
+ */\r
+static bool _topicMatch( const IotLink_t * pSubscriptionLink,\r
+ void * pMatch );\r
+\r
+/**\r
+ * @brief Matches a packet identifier and order.\r
+ *\r
+ * @param[in] pSubscriptionLink Pointer to the link member of an #_mqttSubscription_t.\r
+ * @param[in] pMatch Pointer to a #_packetMatchParams_t.\r
+ *\r
+ * @return `true` if the arguments match the subscription's packet info; `false`\r
+ * otherwise.\r
+ */\r
+static bool _packetMatch( const IotLink_t * pSubscriptionLink,\r
+ void * pMatch );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _topicMatch( const IotLink_t * pSubscriptionLink,\r
+ void * pMatch )\r
+{\r
+ IOT_FUNCTION_ENTRY( bool, false );\r
+ uint16_t nameIndex = 0, filterIndex = 0;\r
+\r
+ /* Because this function is called from a container function, the given link\r
+ * must never be NULL. */\r
+ IotMqtt_Assert( pSubscriptionLink != NULL );\r
+\r
+ _mqttSubscription_t * pSubscription = IotLink_Container( _mqttSubscription_t,\r
+ pSubscriptionLink,\r
+ link );\r
+ _topicMatchParams_t * pParam = ( _topicMatchParams_t * ) pMatch;\r
+\r
+ /* Extract the relevant strings and lengths from parameters. */\r
+ const char * pTopicName = pParam->pTopicName;\r
+ const char * pTopicFilter = pSubscription->pTopicFilter;\r
+ const uint16_t topicNameLength = pParam->topicNameLength;\r
+ const uint16_t topicFilterLength = pSubscription->topicFilterLength;\r
+\r
+ /* Check for an exact match. */\r
+ if( topicNameLength == topicFilterLength )\r
+ {\r
+ status = ( strncmp( pTopicName, pTopicFilter, topicNameLength ) == 0 );\r
+\r
+ IOT_GOTO_CLEANUP();\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* If the topic lengths are different but an exact match is required, return\r
+ * false. */\r
+ if( pParam->exactMatchOnly == true )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ while( ( nameIndex < topicNameLength ) && ( filterIndex < topicFilterLength ) )\r
+ {\r
+ /* Check if the character in the topic name matches the corresponding\r
+ * character in the topic filter string. */\r
+ if( pTopicName[ nameIndex ] == pTopicFilter[ filterIndex ] )\r
+ {\r
+ /* Handle special corner cases as documented by the MQTT protocol spec. */\r
+\r
+ /* Filter "sport/#" also matches "sport" since # includes the parent level. */\r
+ if( nameIndex == topicNameLength - 1 )\r
+ {\r
+ if( filterIndex == topicFilterLength - 3 )\r
+ {\r
+ if( pTopicFilter[ filterIndex + 1 ] == '/' )\r
+ {\r
+ if( pTopicFilter[ filterIndex + 2 ] == '#' )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( true );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Filter "sport/+" also matches the "sport/" but not "sport". */\r
+ if( nameIndex == topicNameLength - 1 )\r
+ {\r
+ if( filterIndex == topicFilterLength - 2 )\r
+ {\r
+ if( pTopicFilter[ filterIndex + 1 ] == '+' )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( true );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* Check for wildcards. */\r
+ if( pTopicFilter[ filterIndex ] == '+' )\r
+ {\r
+ /* Move topic name index to the end of the current level.\r
+ * This is identified by '/'. */\r
+ while( nameIndex < topicNameLength && pTopicName[ nameIndex ] != '/' )\r
+ {\r
+ nameIndex++;\r
+ }\r
+\r
+ /* Increment filter index to skip '/'. */\r
+ filterIndex++;\r
+ continue;\r
+ }\r
+ else if( pTopicFilter[ filterIndex ] == '#' )\r
+ {\r
+ /* Subsequent characters don't need to be checked if the for the\r
+ * multi-level wildcard. */\r
+ IOT_SET_AND_GOTO_CLEANUP( true );\r
+ }\r
+ else\r
+ {\r
+ /* Any character mismatch other than '+' or '#' means the topic\r
+ * name does not match the topic filter. */\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ }\r
+\r
+ /* Increment indexes. */\r
+ nameIndex++;\r
+ filterIndex++;\r
+ }\r
+\r
+ /* If the end of both strings has been reached, they match. */\r
+ if( ( nameIndex == topicNameLength ) && ( filterIndex == topicFilterLength ) )\r
+ {\r
+ IOT_SET_AND_GOTO_CLEANUP( true );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static bool _packetMatch( const IotLink_t * pSubscriptionLink,\r
+ void * pMatch )\r
+{\r
+ bool match = false;\r
+\r
+ /* Because this function is called from a container function, the given link\r
+ * must never be NULL. */\r
+ IotMqtt_Assert( pSubscriptionLink != NULL );\r
+\r
+ _mqttSubscription_t * pSubscription = IotLink_Container( _mqttSubscription_t,\r
+ pSubscriptionLink,\r
+ link );\r
+ _packetMatchParams_t * pParam = ( _packetMatchParams_t * ) pMatch;\r
+\r
+ /* Compare packet identifiers. */\r
+ if( pParam->packetIdentifier == pSubscription->packetInfo.identifier )\r
+ {\r
+ /* Compare orders if order is not -1. */\r
+ if( pParam->order == -1 )\r
+ {\r
+ match = true;\r
+ }\r
+ else\r
+ {\r
+ match = ( ( size_t ) pParam->order ) == pSubscription->packetInfo.order;\r
+ }\r
+ }\r
+\r
+ /* If this subscription should be removed, check the reference count. */\r
+ if( match == true )\r
+ {\r
+ /* Reference count must not be negative. */\r
+ IotMqtt_Assert( pSubscription->references >= 0 );\r
+\r
+ /* If the reference count is positive, this subscription cannot be\r
+ * removed yet because there are subscription callbacks using it. */\r
+ if( pSubscription->references > 0 )\r
+ {\r
+ match = false;\r
+\r
+ /* Set the unsubscribed flag. The last active subscription callback\r
+ * will remove and clean up this subscription. */\r
+ pSubscription->unsubscribed = true;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return match;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+IotMqttError_t _IotMqtt_AddSubscriptions( _mqttConnection_t * pMqttConnection,\r
+ uint16_t subscribePacketIdentifier,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount )\r
+{\r
+ IotMqttError_t status = IOT_MQTT_SUCCESS;\r
+ size_t i = 0;\r
+ _mqttSubscription_t * pNewSubscription = NULL;\r
+ IotLink_t * pSubscriptionLink = NULL;\r
+ _topicMatchParams_t topicMatchParams = { .exactMatchOnly = true };\r
+\r
+ IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );\r
+\r
+ for( i = 0; i < subscriptionCount; i++ )\r
+ {\r
+ /* Check if this topic filter is already registered. */\r
+ topicMatchParams.pTopicName = pSubscriptionList[ i ].pTopicFilter;\r
+ topicMatchParams.topicNameLength = pSubscriptionList[ i ].topicFilterLength;\r
+ pSubscriptionLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),\r
+ NULL,\r
+ _topicMatch,\r
+ &topicMatchParams );\r
+\r
+ if( pSubscriptionLink != NULL )\r
+ {\r
+ pNewSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );\r
+\r
+ /* The lengths of exactly matching topic filters must match. */\r
+ IotMqtt_Assert( pNewSubscription->topicFilterLength == pSubscriptionList[ i ].topicFilterLength );\r
+\r
+ /* Replace the callback and packet info with the new parameters. */\r
+ pNewSubscription->callback = pSubscriptionList[ i ].callback;\r
+ pNewSubscription->packetInfo.identifier = subscribePacketIdentifier;\r
+ pNewSubscription->packetInfo.order = i;\r
+ }\r
+ else\r
+ {\r
+ /* Allocate memory for a new subscription. */\r
+ pNewSubscription = IotMqtt_MallocSubscription( sizeof( _mqttSubscription_t ) +\r
+ pSubscriptionList[ i ].topicFilterLength );\r
+\r
+ if( pNewSubscription == NULL )\r
+ {\r
+ status = IOT_MQTT_NO_MEMORY;\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ /* Clear the new subscription. */\r
+ ( void ) memset( pNewSubscription,\r
+ 0x00,\r
+ sizeof( _mqttSubscription_t ) + pSubscriptionList[ i ].topicFilterLength );\r
+\r
+ /* Set the members of the new subscription and add it to the list. */\r
+ pNewSubscription->packetInfo.identifier = subscribePacketIdentifier;\r
+ pNewSubscription->packetInfo.order = i;\r
+ pNewSubscription->callback = pSubscriptionList[ i ].callback;\r
+ pNewSubscription->topicFilterLength = pSubscriptionList[ i ].topicFilterLength;\r
+ ( void ) memcpy( pNewSubscription->pTopicFilter,\r
+ pSubscriptionList[ i ].pTopicFilter,\r
+ ( size_t ) ( pSubscriptionList[ i ].topicFilterLength ) );\r
+\r
+ IotListDouble_InsertHead( &( pMqttConnection->subscriptionList ),\r
+ &( pNewSubscription->link ) );\r
+ }\r
+ }\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );\r
+\r
+ /* If memory allocation failed, remove all previously added subscriptions. */\r
+ if( status != IOT_MQTT_SUCCESS )\r
+ {\r
+ _IotMqtt_RemoveSubscriptionByTopicFilter( pMqttConnection,\r
+ pSubscriptionList,\r
+ i );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_InvokeSubscriptionCallback( _mqttConnection_t * pMqttConnection,\r
+ IotMqttCallbackParam_t * pCallbackParam )\r
+{\r
+ _mqttSubscription_t * pSubscription = NULL;\r
+ IotLink_t * pCurrentLink = NULL, * pNextLink = NULL;\r
+ void * pCallbackContext = NULL;\r
+\r
+ void ( * callbackFunction )( void *,\r
+ IotMqttCallbackParam_t * ) = NULL;\r
+ _topicMatchParams_t topicMatchParams =\r
+ {\r
+ .pTopicName = pCallbackParam->u.message.info.pTopicName,\r
+ .topicNameLength = pCallbackParam->u.message.info.topicNameLength,\r
+ .exactMatchOnly = false\r
+ };\r
+\r
+ /* Prevent any other thread from modifying the subscription list while this\r
+ * function is searching. */\r
+ IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );\r
+\r
+ /* Search the subscription list for all matching subscriptions starting at\r
+ * the list head. */\r
+ while( true )\r
+ {\r
+ pCurrentLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),\r
+ pCurrentLink,\r
+ _topicMatch,\r
+ &topicMatchParams );\r
+\r
+ /* No subscription found. Exit loop. */\r
+ if( pCurrentLink == NULL )\r
+ {\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Subscription found. Calculate pointer to subscription object. */\r
+ pSubscription = IotLink_Container( _mqttSubscription_t, pCurrentLink, link );\r
+\r
+ /* Subscription validation should not have allowed a NULL callback function. */\r
+ IotMqtt_Assert( pSubscription->callback.function != NULL );\r
+\r
+ /* Increment the subscription's reference count. */\r
+ ( pSubscription->references )++;\r
+\r
+ /* Copy the necessary members of the subscription before releasing the\r
+ * subscription list mutex. */\r
+ pCallbackContext = pSubscription->callback.pCallbackContext;\r
+ callbackFunction = pSubscription->callback.function;\r
+\r
+ /* Unlock the subscription list mutex. */\r
+ IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );\r
+\r
+ /* Set the members of the callback parameter. */\r
+ pCallbackParam->mqttConnection = pMqttConnection;\r
+ pCallbackParam->u.message.pTopicFilter = pSubscription->pTopicFilter;\r
+ pCallbackParam->u.message.topicFilterLength = pSubscription->topicFilterLength;\r
+\r
+ /* Invoke the subscription callback. */\r
+ callbackFunction( pCallbackContext, pCallbackParam );\r
+\r
+ /* Lock the subscription list mutex to decrement the reference count. */\r
+ IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );\r
+\r
+ /* Decrement the reference count. It must still be positive. */\r
+ ( pSubscription->references )--;\r
+ IotMqtt_Assert( pSubscription->references >= 0 );\r
+\r
+ /* Save the pointer to the next link in case this subscription is freed. */\r
+ pNextLink = pCurrentLink->pNext;\r
+\r
+ /* Remove this subscription if it has no references and the unsubscribed\r
+ * flag is set. */\r
+ if( pSubscription->unsubscribed == true )\r
+ {\r
+ /* An unsubscribed subscription should have been removed from the list. */\r
+ IotMqtt_Assert( IotLink_IsLinked( &( pSubscription->link ) ) == false );\r
+\r
+ /* Free subscriptions with no references. */\r
+ if( pSubscription->references == 0 )\r
+ {\r
+ IotMqtt_FreeSubscription( pSubscription );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Move current link pointer. */\r
+ pCurrentLink = pNextLink;\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );\r
+\r
+ _IotMqtt_DecrementConnectionReferences( pMqttConnection );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_RemoveSubscriptionByPacket( _mqttConnection_t * pMqttConnection,\r
+ uint16_t packetIdentifier,\r
+ int32_t order )\r
+{\r
+ const _packetMatchParams_t packetMatchParams =\r
+ {\r
+ .packetIdentifier = packetIdentifier,\r
+ .order = order\r
+ };\r
+\r
+ IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );\r
+ IotListDouble_RemoveAllMatches( &( pMqttConnection->subscriptionList ),\r
+ _packetMatch,\r
+ ( void * ) ( &packetMatchParams ),\r
+ IotMqtt_FreeSubscription,\r
+ offsetof( _mqttSubscription_t, link ) );\r
+ IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+void _IotMqtt_RemoveSubscriptionByTopicFilter( _mqttConnection_t * pMqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount )\r
+{\r
+ size_t i = 0;\r
+ _mqttSubscription_t * pSubscription = NULL;\r
+ IotLink_t * pSubscriptionLink = NULL;\r
+ _topicMatchParams_t topicMatchParams = { 0 };\r
+\r
+ /* Prevent any other thread from modifying the subscription list while this\r
+ * function is running. */\r
+ IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );\r
+\r
+ /* Find and remove each topic filter from the list. */\r
+ for( i = 0; i < subscriptionCount; i++ )\r
+ {\r
+ topicMatchParams.pTopicName = pSubscriptionList[ i ].pTopicFilter;\r
+ topicMatchParams.topicNameLength = pSubscriptionList[ i ].topicFilterLength;\r
+ topicMatchParams.exactMatchOnly = true;\r
+\r
+ pSubscriptionLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),\r
+ NULL,\r
+ _topicMatch,\r
+ &topicMatchParams );\r
+\r
+ if( pSubscriptionLink != NULL )\r
+ {\r
+ pSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );\r
+\r
+ /* Reference count must not be negative. */\r
+ IotMqtt_Assert( pSubscription->references >= 0 );\r
+\r
+ /* Remove subscription from list. */\r
+ IotListDouble_Remove( pSubscriptionLink );\r
+\r
+ /* Check the reference count. This subscription cannot be removed if\r
+ * there are subscription callbacks using it. */\r
+ if( pSubscription->references > 0 )\r
+ {\r
+ /* Set the unsubscribed flag. The last active subscription callback\r
+ * will remove and clean up this subscription. */\r
+ pSubscription->unsubscribed = true;\r
+ }\r
+ else\r
+ {\r
+ /* Free a subscription with no references. */\r
+ IotMqtt_FreeSubscription( pSubscription );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+\r
+ IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+bool IotMqtt_IsSubscribed( IotMqttConnection_t mqttConnection,\r
+ const char * pTopicFilter,\r
+ uint16_t topicFilterLength,\r
+ IotMqttSubscription_t * pCurrentSubscription )\r
+{\r
+ bool status = false;\r
+ _mqttSubscription_t * pSubscription = NULL;\r
+ IotLink_t * pSubscriptionLink = NULL;\r
+ _topicMatchParams_t topicMatchParams =\r
+ {\r
+ .pTopicName = pTopicFilter,\r
+ .topicNameLength = topicFilterLength,\r
+ .exactMatchOnly = true\r
+ };\r
+\r
+ /* Prevent any other thread from modifying the subscription list while this\r
+ * function is running. */\r
+ IotMutex_Lock( &( mqttConnection->subscriptionMutex ) );\r
+\r
+ /* Search for a matching subscription. */\r
+ pSubscriptionLink = IotListDouble_FindFirstMatch( &( mqttConnection->subscriptionList ),\r
+ NULL,\r
+ _topicMatch,\r
+ &topicMatchParams );\r
+\r
+ /* Check if a matching subscription was found. */\r
+ if( pSubscriptionLink != NULL )\r
+ {\r
+ pSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );\r
+\r
+ /* Copy the matching subscription to the output parameter. */\r
+ if( pCurrentSubscription != NULL )\r
+ {\r
+ pCurrentSubscription->pTopicFilter = pTopicFilter;\r
+ pCurrentSubscription->topicFilterLength = topicFilterLength;\r
+ pCurrentSubscription->qos = IOT_MQTT_QOS_0;\r
+ pCurrentSubscription->callback = pSubscription->callback;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ status = true;\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IotMutex_Unlock( &( mqttConnection->subscriptionMutex ) );\r
+\r
+ return status;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/* Provide access to internal functions and variables if testing. */\r
+#if IOT_BUILD_TESTS == 1\r
+ #include "iot_test_access_mqtt_subscription.c"\r
+#endif\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_validate.c\r
+ * @brief Implements functions that validate the structs of the MQTT library.\r
+ */\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Error handling include. */\r
+#include "private/iot_error.h"\r
+\r
+/* MQTT internal include. */\r
+#include "private/iot_mqtt_internal.h"\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+bool _IotMqtt_ValidateConnect( const IotMqttConnectInfo_t * pConnectInfo )\r
+{\r
+ IOT_FUNCTION_ENTRY( bool, true );\r
+\r
+ /* Check for NULL. */\r
+ if( pConnectInfo == NULL )\r
+ {\r
+ IotLogError( "MQTT connection information cannot be NULL." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check that a client identifier was set. */\r
+ if( pConnectInfo->pClientIdentifier == NULL )\r
+ {\r
+ IotLogError( "Client identifier must be set." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check for a zero-length client identifier. Zero-length client identifiers\r
+ * are not allowed with clean sessions. */\r
+ if( pConnectInfo->clientIdentifierLength == 0 )\r
+ {\r
+ IotLogWarn( "A zero-length client identifier was provided." );\r
+\r
+ if( pConnectInfo->cleanSession == true )\r
+ {\r
+ IotLogError( "A zero-length client identifier cannot be used with a clean session." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check that the number of persistent session subscriptions is valid. */\r
+ if( pConnectInfo->cleanSession == false )\r
+ {\r
+ if( pConnectInfo->pPreviousSubscriptions != NULL )\r
+ {\r
+ if( pConnectInfo->previousSubscriptionCount == 0 )\r
+ {\r
+ IotLogError( "Previous subscription count cannot be 0." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* In MQTT 3.1.1, servers are not obligated to accept client identifiers longer\r
+ * than 23 characters. */\r
+ if( pConnectInfo->clientIdentifierLength > 23 )\r
+ {\r
+ IotLogWarn( "A client identifier length of %hu is longer than 23, which is "\r
+ "the longest client identifier a server must accept.",\r
+ pConnectInfo->clientIdentifierLength );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check for compatibility with the AWS IoT MQTT service limits. */\r
+ if( pConnectInfo->awsIotMqttMode == true )\r
+ {\r
+ /* Check that client identifier is within the service limit. */\r
+ if( pConnectInfo->clientIdentifierLength > AWS_IOT_MQTT_SERVER_MAX_CLIENTID )\r
+ {\r
+ IotLogError( "AWS IoT does not support client identifiers longer than %d bytes.",\r
+ AWS_IOT_MQTT_SERVER_MAX_CLIENTID );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check for compliance with AWS IoT keep-alive limits. */\r
+ if( pConnectInfo->keepAliveSeconds == 0 )\r
+ {\r
+ IotLogWarn( "AWS IoT does not support disabling keep-alive. Default keep-alive "\r
+ "of %d seconds will be used.",\r
+ AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE );\r
+ }\r
+ else if( pConnectInfo->keepAliveSeconds < AWS_IOT_MQTT_SERVER_MIN_KEEPALIVE )\r
+ {\r
+ IotLogWarn( "AWS IoT does not support keep-alive intervals less than %d seconds. "\r
+ "An interval of %d seconds will be used.",\r
+ AWS_IOT_MQTT_SERVER_MIN_KEEPALIVE,\r
+ AWS_IOT_MQTT_SERVER_MIN_KEEPALIVE );\r
+ }\r
+ else if( pConnectInfo->keepAliveSeconds > AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE )\r
+ {\r
+ IotLogWarn( "AWS IoT does not support keep-alive intervals greater than %d seconds. "\r
+ "An interval of %d seconds will be used.",\r
+ AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE,\r
+ AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+bool _IotMqtt_ValidatePublish( bool awsIotMqttMode,\r
+ const IotMqttPublishInfo_t * pPublishInfo )\r
+{\r
+ IOT_FUNCTION_ENTRY( bool, true );\r
+\r
+ /* Check for NULL. */\r
+ if( pPublishInfo == NULL )\r
+ {\r
+ IotLogError( "Publish information cannot be NULL." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check topic name for NULL or zero-length. */\r
+ if( pPublishInfo->pTopicName == NULL )\r
+ {\r
+ IotLogError( "Publish topic name must be set." );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pPublishInfo->topicNameLength == 0 )\r
+ {\r
+ IotLogError( "Publish topic name length cannot be 0." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Only allow NULL payloads with zero length. */\r
+ if( pPublishInfo->pPayload == NULL )\r
+ {\r
+ if( pPublishInfo->payloadLength != 0 )\r
+ {\r
+ IotLogError( "Nonzero payload length cannot have a NULL payload." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check for a valid QoS. */\r
+ if( pPublishInfo->qos != IOT_MQTT_QOS_0 )\r
+ {\r
+ if( pPublishInfo->qos != IOT_MQTT_QOS_1 )\r
+ {\r
+ IotLogError( "Publish QoS must be either 0 or 1." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check the retry parameters. */\r
+ if( pPublishInfo->retryLimit > 0 )\r
+ {\r
+ if( pPublishInfo->retryMs == 0 )\r
+ {\r
+ IotLogError( "Publish retry time must be positive." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check for compatibility with AWS IoT MQTT server. */\r
+ if( awsIotMqttMode == true )\r
+ {\r
+ /* Check for retained message. */\r
+ if( pPublishInfo->retain == true )\r
+ {\r
+ IotLogError( "AWS IoT does not support retained publish messages." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check topic name length. */\r
+ if( pPublishInfo->topicNameLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )\r
+ {\r
+ IotLogError( "AWS IoT does not support topic names longer than %d bytes.",\r
+ AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+bool _IotMqtt_ValidateOperation( IotMqttOperation_t operation )\r
+{\r
+ IOT_FUNCTION_ENTRY( bool, true );\r
+\r
+ /* Check for NULL. */\r
+ if( operation == NULL )\r
+ {\r
+ IotLogError( "Operation reference cannot be NULL." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check that reference is waitable. */\r
+ if( ( operation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE ) != IOT_MQTT_FLAG_WAITABLE )\r
+ {\r
+ IotLogError( "Operation is not waitable." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+bool _IotMqtt_ValidateSubscriptionList( IotMqttOperationType_t operation,\r
+ bool awsIotMqttMode,\r
+ const IotMqttSubscription_t * pListStart,\r
+ size_t listSize )\r
+{\r
+ IOT_FUNCTION_ENTRY( bool, true );\r
+ size_t i = 0;\r
+ uint16_t j = 0;\r
+ const IotMqttSubscription_t * pListElement = NULL;\r
+\r
+ /* Operation must be either subscribe or unsubscribe. */\r
+ IotMqtt_Assert( ( operation == IOT_MQTT_SUBSCRIBE ) ||\r
+ ( operation == IOT_MQTT_UNSUBSCRIBE ) );\r
+\r
+ /* Check for empty list. */\r
+ if( pListStart == NULL )\r
+ {\r
+ IotLogError( "Subscription list pointer cannot be NULL." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( listSize == 0 )\r
+ {\r
+ IotLogError( "Empty subscription list." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* AWS IoT supports at most 8 topic filters in a single SUBSCRIBE packet. */\r
+ if( awsIotMqttMode == true )\r
+ {\r
+ if( listSize > AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE )\r
+ {\r
+ IotLogError( "AWS IoT does not support more than %d topic filters per "\r
+ "subscription request.",\r
+ AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ for( i = 0; i < listSize; i++ )\r
+ {\r
+ pListElement = &( pListStart[ i ] );\r
+\r
+ /* Check for a valid QoS and callback function when subscribing. */\r
+ if( operation == IOT_MQTT_SUBSCRIBE )\r
+ {\r
+ if( pListElement->qos != IOT_MQTT_QOS_0 )\r
+ {\r
+ if( pListElement->qos != IOT_MQTT_QOS_1 )\r
+ {\r
+ IotLogError( "Subscription QoS must be either 0 or 1." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pListElement->callback.function == NULL )\r
+ {\r
+ IotLogError( "Callback function must be set." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check subscription topic filter. */\r
+ if( pListElement->pTopicFilter == NULL )\r
+ {\r
+ IotLogError( "Subscription topic filter cannot be NULL." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ if( pListElement->topicFilterLength == 0 )\r
+ {\r
+ IotLogError( "Subscription topic filter length cannot be 0." );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check for compatibility with AWS IoT MQTT server. */\r
+ if( awsIotMqttMode == true )\r
+ {\r
+ /* Check topic filter length. */\r
+ if( pListElement->topicFilterLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )\r
+ {\r
+ IotLogError( "AWS IoT does not support topic filters longer than %d bytes.",\r
+ AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Check that the wildcards '+' and '#' are being used correctly. */\r
+ for( j = 0; j < pListElement->topicFilterLength; j++ )\r
+ {\r
+ switch( pListElement->pTopicFilter[ j ] )\r
+ {\r
+ /* Check that the single level wildcard '+' is used correctly. */\r
+ case '+':\r
+\r
+ /* Unless '+' is the first character in the filter, it must be preceded by '/'. */\r
+ if( j > 0 )\r
+ {\r
+ if( pListElement->pTopicFilter[ j - 1 ] != '/' )\r
+ {\r
+ IotLogError( "Invalid topic filter %.*s -- '+' must be preceded by '/'.",\r
+ pListElement->topicFilterLength,\r
+ pListElement->pTopicFilter );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Unless '+' is the last character in the filter, it must be succeeded by '/'. */\r
+ if( j < pListElement->topicFilterLength - 1 )\r
+ {\r
+ if( pListElement->pTopicFilter[ j + 1 ] != '/' )\r
+ {\r
+ IotLogError( "Invalid topic filter %.*s -- '+' must be succeeded by '/'.",\r
+ pListElement->topicFilterLength,\r
+ pListElement->pTopicFilter );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ break;\r
+\r
+ /* Check that the multi-level wildcard '#' is used correctly. */\r
+ case '#':\r
+\r
+ /* '#' must be the last character in the filter. */\r
+ if( j != pListElement->topicFilterLength - 1 )\r
+ {\r
+ IotLogError( "Invalid topic filter %.*s -- '#' must be the last character.",\r
+ pListElement->topicFilterLength,\r
+ pListElement->pTopicFilter );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ /* Unless '#' is standalone, it must be preceded by '/'. */\r
+ if( pListElement->topicFilterLength > 1 )\r
+ {\r
+ if( pListElement->pTopicFilter[ j - 1 ] != '/' )\r
+ {\r
+ IotLogError( "Invalid topic filter %.*s -- '#' must be preceded by '/'.",\r
+ pListElement->topicFilterLength,\r
+ pListElement->pTopicFilter );\r
+\r
+ IOT_SET_AND_GOTO_CLEANUP( false );\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ EMPTY_ELSE_MARKER;\r
+ }\r
+\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ IOT_FUNCTION_EXIT_NO_CLEANUP();\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
--- /dev/null
+/*\r
+ * Amazon FreeRTOS MQTT V2.0.0\r
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://aws.amazon.com/freertos\r
+ * http://www.FreeRTOS.org\r
+ */\r
+\r
+/**\r
+ * @file iot_mqtt_internal.h\r
+ * @brief Internal header of MQTT library. This header should not be included in\r
+ * typical application code.\r
+ */\r
+\r
+#ifndef IOT_MQTT_INTERNAL_H_\r
+#define IOT_MQTT_INTERNAL_H_\r
+\r
+/* The config header is always included first. */\r
+#include "iot_config.h"\r
+\r
+/* Linear containers (lists and queues) include. */\r
+#include "iot_linear_containers.h"\r
+\r
+/* MQTT include. */\r
+#include "iot_mqtt.h"\r
+\r
+/* Task pool include. */\r
+#include "iot_taskpool.h"\r
+\r
+/**\r
+ * @def IotMqtt_Assert( expression )\r
+ * @brief Assertion macro for the MQTT library.\r
+ *\r
+ * Set @ref IOT_MQTT_ENABLE_ASSERTS to `1` to enable assertions in the MQTT\r
+ * library.\r
+ *\r
+ * @param[in] expression Expression to be evaluated.\r
+ */\r
+#if IOT_MQTT_ENABLE_ASSERTS == 1\r
+ #ifndef IotMqtt_Assert\r
+ #include <assert.h>\r
+ #define IotMqtt_Assert( expression ) assert( expression )\r
+ #endif\r
+#else\r
+ #define IotMqtt_Assert( expression )\r
+#endif\r
+\r
+/* Configure logs for MQTT functions. */\r
+#ifdef IOT_LOG_LEVEL_MQTT\r
+ #define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_MQTT\r
+#else\r
+ #ifdef IOT_LOG_LEVEL_GLOBAL\r
+ #define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL\r
+ #else\r
+ #define LIBRARY_LOG_LEVEL IOT_LOG_NONE\r
+ #endif\r
+#endif\r
+\r
+#define LIBRARY_LOG_NAME ( "MQTT" )\r
+#include "iot_logging_setup.h"\r
+\r
+/*\r
+ * Provide default values for undefined memory allocation functions based on\r
+ * the usage of dynamic memory allocation.\r
+ */\r
+#if IOT_STATIC_MEMORY_ONLY == 1\r
+ #include "private/iot_static_memory.h"\r
+\r
+/**\r
+ * @brief Allocate an #_mqttConnection_t. This function should have the same\r
+ * signature as [malloc]\r
+ * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).\r
+ */\r
+ void * IotMqtt_MallocConnection( size_t size );\r
+\r
+/**\r
+ * @brief Free an #_mqttConnection_t. This function should have the same\r
+ * signature as [free]\r
+ * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).\r
+ */\r
+ void IotMqtt_FreeConnection( void * ptr );\r
+\r
+/**\r
+ * @brief Allocate memory for an MQTT packet. This function should have the\r
+ * same signature as [malloc]\r
+ * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).\r
+ */\r
+ #define IotMqtt_MallocMessage Iot_MallocMessageBuffer\r
+\r
+/**\r
+ * @brief Free an MQTT packet. This function should have the same signature\r
+ * as [free]\r
+ * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).\r
+ */\r
+ #define IotMqtt_FreeMessage Iot_FreeMessageBuffer\r
+\r
+/**\r
+ * @brief Allocate an #_mqttOperation_t. This function should have the same\r
+ * signature as [malloc]\r
+ * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).\r
+ */\r
+ void * IotMqtt_MallocOperation( size_t size );\r
+\r
+/**\r
+ * @brief Free an #_mqttOperation_t. This function should have the same\r
+ * signature as [free]\r
+ * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).\r
+ */\r
+ void IotMqtt_FreeOperation( void * ptr );\r
+\r
+/**\r
+ * @brief Allocate an #_mqttSubscription_t. This function should have the\r
+ * same signature as [malloc]\r
+ * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).\r
+ */\r
+ void * IotMqtt_MallocSubscription( size_t size );\r
+\r
+/**\r
+ * @brief Free an #_mqttSubscription_t. This function should have the same\r
+ * signature as [free]\r
+ * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).\r
+ */\r
+ void IotMqtt_FreeSubscription( void * ptr );\r
+#else /* if IOT_STATIC_MEMORY_ONLY == 1 */\r
+ #include <stdlib.h>\r
+\r
+ #ifndef IotMqtt_MallocConnection\r
+ #define IotMqtt_MallocConnection malloc\r
+ #endif\r
+\r
+ #ifndef IotMqtt_FreeConnection\r
+ #define IotMqtt_FreeConnection free\r
+ #endif\r
+\r
+ #ifndef IotMqtt_MallocMessage\r
+ #define IotMqtt_MallocMessage malloc\r
+ #endif\r
+\r
+ #ifndef IotMqtt_FreeMessage\r
+ #define IotMqtt_FreeMessage free\r
+ #endif\r
+\r
+ #ifndef IotMqtt_MallocOperation\r
+ #define IotMqtt_MallocOperation malloc\r
+ #endif\r
+\r
+ #ifndef IotMqtt_FreeOperation\r
+ #define IotMqtt_FreeOperation free\r
+ #endif\r
+\r
+ #ifndef IotMqtt_MallocSubscription\r
+ #define IotMqtt_MallocSubscription malloc\r
+ #endif\r
+\r
+ #ifndef IotMqtt_FreeSubscription\r
+ #define IotMqtt_FreeSubscription free\r
+ #endif\r
+#endif /* if IOT_STATIC_MEMORY_ONLY == 1 */\r
+\r
+/**\r
+ * @cond DOXYGEN_IGNORE\r
+ * Doxygen should ignore this section.\r
+ *\r
+ * Provide default values for undefined configuration constants.\r
+ */\r
+#ifndef AWS_IOT_MQTT_ENABLE_METRICS\r
+ #define AWS_IOT_MQTT_ENABLE_METRICS ( 1 )\r
+#endif\r
+#ifndef IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES\r
+ #define IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES ( 0 )\r
+#endif\r
+#ifndef IOT_MQTT_RESPONSE_WAIT_MS\r
+ #define IOT_MQTT_RESPONSE_WAIT_MS ( 1000 )\r
+#endif\r
+#ifndef IOT_MQTT_RETRY_MS_CEILING\r
+ #define IOT_MQTT_RETRY_MS_CEILING ( 60000 )\r
+#endif\r
+/** @endcond */\r
+\r
+/**\r
+ * @brief Marks the empty statement of an `else` branch.\r
+ *\r
+ * Does nothing, but allows test coverage to detect branches not taken. By default,\r
+ * this is defined to nothing. When running code coverage testing, this is defined\r
+ * to an assembly NOP.\r
+ */\r
+#ifndef EMPTY_ELSE_MARKER\r
+ #define EMPTY_ELSE_MARKER\r
+#endif\r
+\r
+/*\r
+ * Constants related to limits defined in AWS Service Limits.\r
+ *\r
+ * For details, see\r
+ * https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html\r
+ *\r
+ * Used to validate parameters if when connecting to an AWS IoT MQTT server.\r
+ */\r
+#define AWS_IOT_MQTT_SERVER_MIN_KEEPALIVE ( 30 ) /**< @brief Minumum keep-alive interval accepted by AWS IoT. */\r
+#define AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE ( 1200 ) /**< @brief Maximum keep-alive interval accepted by AWS IoT. */\r
+#define AWS_IOT_MQTT_SERVER_MAX_CLIENTID ( 128 ) /**< @brief Maximum length of client identifier accepted by AWS IoT. */\r
+#define AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH ( 256 ) /**< @brief Maximum length of topic names or filters accepted by AWS IoT. */\r
+#define AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE ( 8 ) /**< @brief Maximum number of topic filters in a single SUBSCRIBE packet. */\r
+\r
+/*\r
+ * MQTT control packet type and flags. Always the first byte of an MQTT\r
+ * packet.\r
+ *\r
+ * For details, see\r
+ * http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/csprd02/mqtt-v3.1.1-csprd02.html#_Toc385349757\r
+ */\r
+#define MQTT_PACKET_TYPE_CONNECT ( ( uint8_t ) 0x10U ) /**< @brief CONNECT (client-to-server). */\r
+#define MQTT_PACKET_TYPE_CONNACK ( ( uint8_t ) 0x20U ) /**< @brief CONNACK (server-to-client). */\r
+#define MQTT_PACKET_TYPE_PUBLISH ( ( uint8_t ) 0x30U ) /**< @brief PUBLISH (bi-directional). */\r
+#define MQTT_PACKET_TYPE_PUBACK ( ( uint8_t ) 0x40U ) /**< @brief PUBACK (server-to-client). */\r
+#define MQTT_PACKET_TYPE_SUBSCRIBE ( ( uint8_t ) 0x82U ) /**< @brief SUBSCRIBE (client-to-server). */\r
+#define MQTT_PACKET_TYPE_SUBACK ( ( uint8_t ) 0x90U ) /**< @brief SUBACK (server-to-client). */\r
+#define MQTT_PACKET_TYPE_UNSUBSCRIBE ( ( uint8_t ) 0xa2U ) /**< @brief UNSUBSCRIBE (client-to-server). */\r
+#define MQTT_PACKET_TYPE_UNSUBACK ( ( uint8_t ) 0xb0U ) /**< @brief UNSUBACK (server-to-client). */\r
+#define MQTT_PACKET_TYPE_PINGREQ ( ( uint8_t ) 0xc0U ) /**< @brief PINGREQ (client-to-server). */\r
+#define MQTT_PACKET_TYPE_PINGRESP ( ( uint8_t ) 0xd0U ) /**< @brief PINGRESP (server-to-client). */\r
+#define MQTT_PACKET_TYPE_DISCONNECT ( ( uint8_t ) 0xe0U ) /**< @brief DISCONNECT (client-to-server). */\r
+\r
+/**\r
+ * @brief A value that represents an invalid remaining length.\r
+ *\r
+ * This value is greater than what is allowed by the MQTT specification.\r
+ */\r
+#define MQTT_REMAINING_LENGTH_INVALID ( ( size_t ) 268435456 )\r
+\r
+/*---------------------- MQTT internal data structures ----------------------*/\r
+\r
+/**\r
+ * @brief Represents an MQTT connection.\r
+ */\r
+typedef struct _mqttConnection\r
+{\r
+ bool awsIotMqttMode; /**< @brief Specifies if this connection is to an AWS IoT MQTT server. */\r
+ bool ownNetworkConnection; /**< @brief Whether this MQTT connection owns its network connection. */\r
+ void * pNetworkConnection; /**< @brief References the transport-layer network connection. */\r
+ const IotNetworkInterface_t * pNetworkInterface; /**< @brief Network interface provided to @ref mqtt_function_connect. */\r
+ IotMqttCallbackInfo_t disconnectCallback; /**< @brief A function to invoke when this connection is disconnected. */\r
+\r
+ #if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1\r
+ const IotMqttSerializer_t * pSerializer; /**< @brief MQTT packet serializer overrides. */\r
+ #endif\r
+\r
+ bool disconnected; /**< @brief Tracks if this connection has been disconnected. */\r
+ IotMutex_t referencesMutex; /**< @brief Recursive mutex. Grants access to connection state and operation lists. */\r
+ int32_t references; /**< @brief Counts callbacks and operations using this connection. */\r
+ IotListDouble_t pendingProcessing; /**< @brief List of operations waiting to be processed by a task pool routine. */\r
+ IotListDouble_t pendingResponse; /**< @brief List of processed operations awaiting a server response. */\r
+\r
+ IotListDouble_t subscriptionList; /**< @brief Holds subscriptions associated with this connection. */\r
+ IotMutex_t subscriptionMutex; /**< @brief Grants exclusive access to the subscription list. */\r
+\r
+ bool keepAliveFailure; /**< @brief Failure flag for keep-alive operation. */\r
+ uint32_t keepAliveMs; /**< @brief Keep-alive interval in milliseconds. Its max value (per spec) is 65,535,000. */\r
+ uint32_t nextKeepAliveMs; /**< @brief Relative delay for next keep-alive job. */\r
+ IotTaskPoolJobStorage_t keepAliveJobStorage; /**< @brief Task pool job for processing this connection's keep-alive. */\r
+ IotTaskPoolJob_t keepAliveJob; /**< @brief Task pool job for processing this connection's keep-alive. */\r
+ uint8_t * pPingreqPacket; /**< @brief An MQTT PINGREQ packet, allocated if keep-alive is active. */\r
+ size_t pingreqPacketSize; /**< @brief The size of an allocated PINGREQ packet. */\r
+} _mqttConnection_t;\r
+\r
+/**\r
+ * @brief Represents a subscription stored in an MQTT connection.\r
+ */\r
+typedef struct _mqttSubscription\r
+{\r
+ IotLink_t link; /**< @brief List link member. */\r
+\r
+ int32_t references; /**< @brief How many subscription callbacks are using this subscription. */\r
+\r
+ /**\r
+ * @brief Tracks whether @ref mqtt_function_unsubscribe has been called for\r
+ * this subscription.\r
+ *\r
+ * If there are active subscription callbacks, @ref mqtt_function_unsubscribe\r
+ * cannot remove this subscription. Instead, it will set this flag, which\r
+ * schedules the removal of this subscription once all subscription callbacks\r
+ * terminate.\r
+ */\r
+ bool unsubscribed;\r
+\r
+ struct\r
+ {\r
+ uint16_t identifier; /**< @brief Packet identifier. */\r
+ size_t order; /**< @brief Order in the packet's list of subscriptions. */\r
+ } packetInfo; /**< @brief Information about the SUBSCRIBE packet that registered this subscription. */\r
+\r
+ IotMqttCallbackInfo_t callback; /**< @brief Callback information for this subscription. */\r
+\r
+ uint16_t topicFilterLength; /**< @brief Length of #_mqttSubscription_t.pTopicFilter. */\r
+ char pTopicFilter[]; /**< @brief The subscription topic filter. */\r
+} _mqttSubscription_t;\r
+\r
+/**\r
+ * @brief Internal structure representing a single MQTT operation, such as\r
+ * CONNECT, SUBSCRIBE, PUBLISH, etc.\r
+ *\r
+ * Queues of these structures keeps track of all in-progress MQTT operations.\r
+ */\r
+typedef struct _mqttOperation\r
+{\r
+ /* Pointers to neighboring queue elements. */\r
+ IotLink_t link; /**< @brief List link member. */\r
+\r
+ bool incomingPublish; /**< @brief Set to true if this operation an incoming PUBLISH. */\r
+ _mqttConnection_t * pMqttConnection; /**< @brief MQTT connection associated with this operation. */\r
+\r
+ IotTaskPoolJobStorage_t jobStorage; /**< @brief Task pool job storage associated with this operation. */\r
+ IotTaskPoolJob_t job; /**< @brief Task pool job associated with this operation. */\r
+\r
+ union\r
+ {\r
+ /* If incomingPublish is false, this struct is valid. */\r
+ struct\r
+ {\r
+ /* Basic operation information. */\r
+ int32_t jobReference; /**< @brief Tracks if a job is using this operation. Must always be 0, 1, or 2. */\r
+ IotMqttOperationType_t type; /**< @brief What operation this structure represents. */\r
+ uint32_t flags; /**< @brief Flags passed to the function that created this operation. */\r
+ uint16_t packetIdentifier; /**< @brief The packet identifier used with this operation. */\r
+\r
+ /* Serialized packet and size. */\r
+ uint8_t * pMqttPacket; /**< @brief The MQTT packet to send over the network. */\r
+ uint8_t * pPacketIdentifierHigh; /**< @brief The location of the high byte of the packet identifier in the MQTT packet. */\r
+ size_t packetSize; /**< @brief Size of `pMqttPacket`. */\r
+\r
+ /* How to notify of an operation's completion. */\r
+ union\r
+ {\r
+ IotSemaphore_t waitSemaphore; /**< @brief Semaphore to be used with @ref mqtt_function_wait. */\r
+ IotMqttCallbackInfo_t callback; /**< @brief User-provided callback function and parameter. */\r
+ } notify; /**< @brief How to notify of this operation's completion. */\r
+ IotMqttError_t status; /**< @brief Result of this operation. This is reported once a response is received. */\r
+\r
+ struct\r
+ {\r
+ uint32_t count;\r
+ uint32_t limit;\r
+ uint32_t nextPeriod;\r
+ } retry;\r
+ } operation;\r
+\r
+ /* If incomingPublish is true, this struct is valid. */\r
+ struct\r
+ {\r
+ IotMqttPublishInfo_t publishInfo; /**< @brief Deserialized PUBLISH. */\r
+ const void * pReceivedData; /**< @brief Any buffer associated with this PUBLISH that should be freed. */\r
+ } publish;\r
+ } u; /**< @brief Valid member depends on _mqttOperation_t.incomingPublish. */\r
+} _mqttOperation_t;\r
+\r
+/**\r
+ * @brief Represents an MQTT packet received from the network.\r
+ *\r
+ * This struct is used to hold parameters for the deserializers so that all\r
+ * deserializers have the same function signature.\r
+ */\r
+typedef struct _mqttPacket\r
+{\r
+ union\r
+ {\r
+ /**\r
+ * @brief (Input) MQTT connection associated with this packet. Only used\r
+ * when deserializing SUBACKs.\r
+ */\r
+ _mqttConnection_t * pMqttConnection;\r
+\r
+ /**\r
+ * @brief (Output) Operation representing an incoming PUBLISH. Only used\r
+ * when deserializing PUBLISHes.\r
+ */\r
+ _mqttOperation_t * pIncomingPublish;\r
+ } u; /**< @brief Valid member depends on packet being decoded. */\r
+\r
+ uint8_t * pRemainingData; /**< @brief (Input) The remaining data in MQTT packet. */\r
+ size_t remainingLength; /**< @brief (Input) Length of the remaining data in the MQTT packet. */\r
+ uint16_t packetIdentifier; /**< @brief (Output) MQTT packet identifier. */\r
+ uint8_t type; /**< @brief (Input) A value identifying the packet type. */\r
+} _mqttPacket_t;\r
+\r
+/*-------------------- MQTT struct validation functions ---------------------*/\r
+\r
+/**\r
+ * @brief Check that an #IotMqttConnectInfo_t is valid.\r
+ *\r
+ * @param[in] pConnectInfo The #IotMqttConnectInfo_t to validate.\r
+ *\r
+ * @return `true` if `pConnectInfo` is valid; `false` otherwise.\r
+ */\r
+bool _IotMqtt_ValidateConnect( const IotMqttConnectInfo_t * pConnectInfo );\r
+\r
+/**\r
+ * @brief Check that an #IotMqttPublishInfo_t is valid.\r
+ *\r
+ * @param[in] awsIotMqttMode Specifies if this PUBLISH packet is being sent to\r
+ * an AWS IoT MQTT server.\r
+ * @param[in] pPublishInfo The #IotMqttPublishInfo_t to validate.\r
+ *\r
+ * @return `true` if `pPublishInfo` is valid; `false` otherwise.\r
+ */\r
+bool _IotMqtt_ValidatePublish( bool awsIotMqttMode,\r
+ const IotMqttPublishInfo_t * pPublishInfo );\r
+\r
+/**\r
+ * @brief Check that an #IotMqttOperation_t is valid and waitable.\r
+ *\r
+ * @param[in] operation The #IotMqttOperation_t to validate.\r
+ *\r
+ * @return `true` if `operation` is valid; `false` otherwise.\r
+ */\r
+bool _IotMqtt_ValidateOperation( IotMqttOperation_t operation );\r
+\r
+/**\r
+ * @brief Check that a list of #IotMqttSubscription_t is valid.\r
+ *\r
+ * @param[in] operation Either #IOT_MQTT_SUBSCRIBE or #IOT_MQTT_UNSUBSCRIBE.\r
+ * Some parameters are not validated for #IOT_MQTT_UNSUBSCRIBE.\r
+ * @param[in] awsIotMqttMode Specifies if this SUBSCRIBE packet is being sent to\r
+ * an AWS IoT MQTT server.\r
+ * @param[in] pListStart First element of the list to validate.\r
+ * @param[in] listSize Number of elements in the subscription list.\r
+ *\r
+ * @return `true` if every element in the list is valid; `false` otherwise.\r
+ */\r
+bool _IotMqtt_ValidateSubscriptionList( IotMqttOperationType_t operation,\r
+ bool awsIotMqttMode,\r
+ const IotMqttSubscription_t * pListStart,\r
+ size_t listSize );\r
+\r
+/*-------------------- MQTT packet serializer functions ---------------------*/\r
+\r
+/**\r
+ * @brief Get the MQTT packet type from a stream of bytes off the network.\r
+ *\r
+ * @param[in] pNetworkConnection Reference to the network connection.\r
+ * @param[in] pNetworkInterface Function pointers used to interact with the\r
+ * network.\r
+ *\r
+ * @return One of the server-to-client MQTT packet types.\r
+ *\r
+ * @note This function is only used for incoming packets, and may not work\r
+ * correctly for outgoing packets.\r
+ */\r
+uint8_t _IotMqtt_GetPacketType( void * pNetworkConnection,\r
+ const IotNetworkInterface_t * pNetworkInterface );\r
+\r
+/**\r
+ * @brief Get the remaining length from a stream of bytes off the network.\r
+ *\r
+ * @param[in] pNetworkConnection Reference to the network connection.\r
+ * @param[in] pNetworkInterface Function pointers used to interact with the\r
+ * network.\r
+ *\r
+ * @return The remaining length; #MQTT_REMAINING_LENGTH_INVALID on error.\r
+ */\r
+size_t _IotMqtt_GetRemainingLength( void * pNetworkConnection,\r
+ const IotNetworkInterface_t * pNetworkInterface );\r
+\r
+/**\r
+ * @brief Generate a CONNECT packet from the given parameters.\r
+ *\r
+ * @param[in] pConnectInfo User-provided CONNECT information.\r
+ * @param[out] pConnectPacket Where the CONNECT packet is written.\r
+ * @param[out] pPacketSize Size of the packet written to `pConnectPacket`.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.\r
+ */\r
+IotMqttError_t _IotMqtt_SerializeConnect( const IotMqttConnectInfo_t * pConnectInfo,\r
+ uint8_t ** pConnectPacket,\r
+ size_t * pPacketSize );\r
+\r
+/**\r
+ * @brief Deserialize a CONNACK packet.\r
+ *\r
+ * Converts the packet from a stream of bytes to an #IotMqttError_t. Also\r
+ * prints out debug log messages about the packet.\r
+ *\r
+ * @param[in,out] pConnack Pointer to an MQTT packet struct representing a CONNACK.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS if CONNACK specifies that CONNECT was accepted;\r
+ * #IOT_MQTT_SERVER_REFUSED if CONNACK specifies that CONNECT was rejected;\r
+ * #IOT_MQTT_BAD_RESPONSE if the CONNACK packet doesn't follow MQTT spec.\r
+ */\r
+IotMqttError_t _IotMqtt_DeserializeConnack( _mqttPacket_t * pConnack );\r
+\r
+/**\r
+ * @brief Generate a PUBLISH packet from the given parameters.\r
+ *\r
+ * @param[in] pPublishInfo User-provided PUBLISH information.\r
+ * @param[out] pPublishPacket Where the PUBLISH packet is written.\r
+ * @param[out] pPacketSize Size of the packet written to `pPublishPacket`.\r
+ * @param[out] pPacketIdentifier The packet identifier generated for this PUBLISH.\r
+ * @param[out] pPacketIdentifierHigh Where the high byte of the packet identifier\r
+ * is written.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.\r
+ */\r
+IotMqttError_t _IotMqtt_SerializePublish( const IotMqttPublishInfo_t * pPublishInfo,\r
+ uint8_t ** pPublishPacket,\r
+ size_t * pPacketSize,\r
+ uint16_t * pPacketIdentifier,\r
+ uint8_t ** pPacketIdentifierHigh );\r
+\r
+/**\r
+ * @brief Set the DUP bit in a QoS 1 PUBLISH packet.\r
+ *\r
+ * @param[in] pPublishPacket Pointer to the PUBLISH packet to modify.\r
+ * @param[in] pPacketIdentifierHigh The high byte of any packet identifier to modify.\r
+ * @param[out] pNewPacketIdentifier Since AWS IoT does not support the DUP flag,\r
+ * a new packet identifier is generated and should be written here. This parameter\r
+ * is only used when connected to an AWS IoT MQTT server.\r
+ *\r
+ * @note See #IotMqttPublishInfo_t for caveats with retransmission to the\r
+ * AWS IoT MQTT server.\r
+ */\r
+void _IotMqtt_PublishSetDup( uint8_t * pPublishPacket,\r
+ uint8_t * pPacketIdentifierHigh,\r
+ uint16_t * pNewPacketIdentifier );\r
+\r
+/**\r
+ * @brief Deserialize a PUBLISH packet received from the server.\r
+ *\r
+ * Converts the packet from a stream of bytes to an #IotMqttPublishInfo_t and\r
+ * extracts the packet identifier. Also prints out debug log messages about the\r
+ * packet.\r
+ *\r
+ * @param[in,out] pPublish Pointer to an MQTT packet struct representing a PUBLISH.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS if PUBLISH is valid; #IOT_MQTT_BAD_RESPONSE\r
+ * if the PUBLISH packet doesn't follow MQTT spec.\r
+ */\r
+IotMqttError_t _IotMqtt_DeserializePublish( _mqttPacket_t * pPublish );\r
+\r
+/**\r
+ * @brief Generate a PUBACK packet for the given packet identifier.\r
+ *\r
+ * @param[in] packetIdentifier The packet identifier to place in PUBACK.\r
+ * @param[out] pPubackPacket Where the PUBACK packet is written.\r
+ * @param[out] pPacketSize Size of the packet written to `pPubackPacket`.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.\r
+ */\r
+IotMqttError_t _IotMqtt_SerializePuback( uint16_t packetIdentifier,\r
+ uint8_t ** pPubackPacket,\r
+ size_t * pPacketSize );\r
+\r
+/**\r
+ * @brief Deserialize a PUBACK packet.\r
+ *\r
+ * Converts the packet from a stream of bytes to an #IotMqttError_t and extracts\r
+ * the packet identifier. Also prints out debug log messages about the packet.\r
+ *\r
+ * @param[in,out] pPuback Pointer to an MQTT packet struct representing a PUBACK.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS if PUBACK is valid; #IOT_MQTT_BAD_RESPONSE\r
+ * if the PUBACK packet doesn't follow MQTT spec.\r
+ */\r
+IotMqttError_t _IotMqtt_DeserializePuback( _mqttPacket_t * pPuback );\r
+\r
+/**\r
+ * @brief Generate a SUBSCRIBE packet from the given parameters.\r
+ *\r
+ * @param[in] pSubscriptionList User-provided array of subscriptions.\r
+ * @param[in] subscriptionCount Size of `pSubscriptionList`.\r
+ * @param[out] pSubscribePacket Where the SUBSCRIBE packet is written.\r
+ * @param[out] pPacketSize Size of the packet written to `pSubscribePacket`.\r
+ * @param[out] pPacketIdentifier The packet identifier generated for this SUBSCRIBE.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.\r
+ */\r
+IotMqttError_t _IotMqtt_SerializeSubscribe( const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint8_t ** pSubscribePacket,\r
+ size_t * pPacketSize,\r
+ uint16_t * pPacketIdentifier );\r
+\r
+/**\r
+ * @brief Deserialize a SUBACK packet.\r
+ *\r
+ * Converts the packet from a stream of bytes to an #IotMqttError_t and extracts\r
+ * the packet identifier. Also prints out debug log messages about the packet.\r
+ *\r
+ * @param[in,out] pSuback Pointer to an MQTT packet struct representing a SUBACK.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS if SUBACK is valid; #IOT_MQTT_BAD_RESPONSE\r
+ * if the SUBACK packet doesn't follow MQTT spec.\r
+ */\r
+IotMqttError_t _IotMqtt_DeserializeSuback( _mqttPacket_t * pSuback );\r
+\r
+/**\r
+ * @brief Generate an UNSUBSCRIBE packet from the given parameters.\r
+ *\r
+ * @param[in] pSubscriptionList User-provided array of subscriptions to remove.\r
+ * @param[in] subscriptionCount Size of `pSubscriptionList`.\r
+ * @param[out] pUnsubscribePacket Where the UNSUBSCRIBE packet is written.\r
+ * @param[out] pPacketSize Size of the packet written to `pUnsubscribePacket`.\r
+ * @param[out] pPacketIdentifier The packet identifier generated for this UNSUBSCRIBE.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.\r
+ */\r
+IotMqttError_t _IotMqtt_SerializeUnsubscribe( const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount,\r
+ uint8_t ** pUnsubscribePacket,\r
+ size_t * pPacketSize,\r
+ uint16_t * pPacketIdentifier );\r
+\r
+/**\r
+ * @brief Deserialize a UNSUBACK packet.\r
+ *\r
+ * Converts the packet from a stream of bytes to an #IotMqttError_t and extracts\r
+ * the packet identifier. Also prints out debug log messages about the packet.\r
+ *\r
+ * @param[in,out] pUnsuback Pointer to an MQTT packet struct representing an UNSUBACK.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS if UNSUBACK is valid; #IOT_MQTT_BAD_RESPONSE\r
+ * if the UNSUBACK packet doesn't follow MQTT spec.\r
+ */\r
+IotMqttError_t _IotMqtt_DeserializeUnsuback( _mqttPacket_t * pUnsuback );\r
+\r
+/**\r
+ * @brief Generate a PINGREQ packet.\r
+ *\r
+ * @param[out] pPingreqPacket Where the PINGREQ packet is written.\r
+ * @param[out] pPacketSize Size of the packet written to `pPingreqPacket`.\r
+ *\r
+ * @return Always returns #IOT_MQTT_SUCCESS.\r
+ */\r
+IotMqttError_t _IotMqtt_SerializePingreq( uint8_t ** pPingreqPacket,\r
+ size_t * pPacketSize );\r
+\r
+/**\r
+ * @brief Deserialize a PINGRESP packet.\r
+ *\r
+ * Converts the packet from a stream of bytes to an #IotMqttError_t. Also\r
+ * prints out debug log messages about the packet.\r
+ *\r
+ * @param[in,out] pPingresp Pointer to an MQTT packet struct representing a PINGRESP.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS if PINGRESP is valid; #IOT_MQTT_BAD_RESPONSE\r
+ * if the PINGRESP packet doesn't follow MQTT spec.\r
+ */\r
+IotMqttError_t _IotMqtt_DeserializePingresp( _mqttPacket_t * pPingresp );\r
+\r
+/**\r
+ * @brief Generate a DISCONNECT packet.\r
+ *\r
+ * @param[out] pDisconnectPacket Where the DISCONNECT packet is written.\r
+ * @param[out] pPacketSize Size of the packet written to `pDisconnectPacket`.\r
+ *\r
+ * @return Always returns #IOT_MQTT_SUCCESS.\r
+ */\r
+IotMqttError_t _IotMqtt_SerializeDisconnect( uint8_t ** pDisconnectPacket,\r
+ size_t * pPacketSize );\r
+\r
+/**\r
+ * @brief Free a packet generated by the serializer.\r
+ *\r
+ * @param[in] pPacket The packet to free.\r
+ */\r
+void _IotMqtt_FreePacket( uint8_t * pPacket );\r
+\r
+/*-------------------- MQTT operation record functions ----------------------*/\r
+\r
+/**\r
+ * @brief Create a record for a new in-progress MQTT operation.\r
+ *\r
+ * @param[in] pMqttConnection The MQTT connection to associate with the operation.\r
+ * @param[in] flags Flags variable passed to a user-facing MQTT function.\r
+ * @param[in] pCallbackInfo User-provided callback function and parameter.\r
+ * @param[out] pNewOperation Set to point to the new operation on success.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS, #IOT_MQTT_BAD_PARAMETER, or #IOT_MQTT_NO_MEMORY.\r
+ */\r
+IotMqttError_t _IotMqtt_CreateOperation( _mqttConnection_t * pMqttConnection,\r
+ uint32_t flags,\r
+ const IotMqttCallbackInfo_t * pCallbackInfo,\r
+ _mqttOperation_t ** pNewOperation );\r
+\r
+/**\r
+ * @brief Decrement the job reference count of an MQTT operation and optionally\r
+ * cancel its job.\r
+ *\r
+ * Checks if the operation may be destroyed afterwards.\r
+ *\r
+ * @param[in] pOperation The MQTT operation with the job to cancel.\r
+ * @param[in] cancelJob Whether to attempt cancellation of the operation's job.\r
+ *\r
+ * @return `true` if the the operation may be safely destroyed; `false` otherwise.\r
+ */\r
+bool _IotMqtt_DecrementOperationReferences( _mqttOperation_t * pOperation,\r
+ bool cancelJob );\r
+\r
+/**\r
+ * @brief Free resources used to record an MQTT operation. This is called when\r
+ * the operation completes.\r
+ *\r
+ * @param[in] pOperation The operation which completed.\r
+ */\r
+void _IotMqtt_DestroyOperation( _mqttOperation_t * pOperation );\r
+\r
+/**\r
+ * @brief Task pool routine for processing an MQTT connection's keep-alive.\r
+ *\r
+ * @param[in] pTaskPool Pointer to the system task pool.\r
+ * @param[in] pKeepAliveJob Pointer the an MQTT connection's keep-alive job.\r
+ * @param[in] pContext Pointer to an MQTT connection, passed as an opaque context.\r
+ */\r
+void _IotMqtt_ProcessKeepAlive( IotTaskPool_t pTaskPool,\r
+ IotTaskPoolJob_t pKeepAliveJob,\r
+ void * pContext );\r
+\r
+/**\r
+ * @brief Task pool routine for processing an incoming PUBLISH message.\r
+ *\r
+ * @param[in] pTaskPool Pointer to the system task pool.\r
+ * @param[in] pPublishJob Pointer to the incoming PUBLISH operation's job.\r
+ * @param[in] pContext Pointer to the incoming PUBLISH operation, passed as an\r
+ * opaque context.\r
+ */\r
+void _IotMqtt_ProcessIncomingPublish( IotTaskPool_t pTaskPool,\r
+ IotTaskPoolJob_t pPublishJob,\r
+ void * pContext );\r
+\r
+/**\r
+ * @brief Task pool routine for processing an MQTT operation to send.\r
+ *\r
+ * @param[in] pTaskPool Pointer to the system task pool.\r
+ * @param[in] pSendJob Pointer to an operation's job.\r
+ * @param[in] pContext Pointer to the operation to send, passed as an opaque\r
+ * context.\r
+ */\r
+void _IotMqtt_ProcessSend( IotTaskPool_t pTaskPool,\r
+ IotTaskPoolJob_t pSendJob,\r
+ void * pContext );\r
+\r
+/**\r
+ * @brief Task pool routine for processing a completed MQTT operation.\r
+ *\r
+ * @param[in] pTaskPool Pointer to the system task pool.\r
+ * @param[in] pOperationJob Pointer to the completed operation's job.\r
+ * @param[in] pContext Pointer to the completed operation, passed as an opaque\r
+ * context.\r
+ */\r
+void _IotMqtt_ProcessCompletedOperation( IotTaskPool_t pTaskPool,\r
+ IotTaskPoolJob_t pOperationJob,\r
+ void * pContext );\r
+\r
+/**\r
+ * @brief Schedule an operation for immediate processing.\r
+ *\r
+ * @param[in] pOperation The operation to schedule.\r
+ * @param[in] jobRoutine The routine to run for the job. Must be either\r
+ * #_IotMqtt_ProcessSend, #_IotMqtt_ProcessCompletedOperation, or\r
+ * #_IotMqtt_ProcessIncomingPublish.\r
+ * @param[in] delay A delay before the operation job should be executed. Pass\r
+ * `0` to execute ASAP.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS or #IOT_MQTT_SCHEDULING_ERROR.\r
+ */\r
+IotMqttError_t _IotMqtt_ScheduleOperation( _mqttOperation_t * pOperation,\r
+ IotTaskPoolRoutine_t jobRoutine,\r
+ uint32_t delay );\r
+\r
+/**\r
+ * @brief Search a list of MQTT operations pending responses using an operation\r
+ * name and packet identifier. Removes a matching operation from the list if found.\r
+ *\r
+ * @param[in] pMqttConnection The connection associated with the operation.\r
+ * @param[in] type The operation type to look for.\r
+ * @param[in] pPacketIdentifier A packet identifier to match. Pass `NULL` to ignore.\r
+ *\r
+ * @return Pointer to any matching operation; `NULL` if no match was found.\r
+ */\r
+_mqttOperation_t * _IotMqtt_FindOperation( _mqttConnection_t * pMqttConnection,\r
+ IotMqttOperationType_t type,\r
+ const uint16_t * pPacketIdentifier );\r
+\r
+/**\r
+ * @brief Notify of a completed MQTT operation.\r
+ *\r
+ * @param[in] pOperation The MQTT operation which completed.\r
+ *\r
+ * Depending on the parameters passed to a user-facing MQTT function, the\r
+ * notification will cause @ref mqtt_function_wait to return or invoke a\r
+ * user-provided callback.\r
+ */\r
+void _IotMqtt_Notify( _mqttOperation_t * pOperation );\r
+\r
+/*----------------- MQTT subscription management functions ------------------*/\r
+\r
+/**\r
+ * @brief Add an array of subscriptions to the subscription manager.\r
+ *\r
+ * @param[in] pMqttConnection The MQTT connection associated with the subscriptions.\r
+ * @param[in] subscribePacketIdentifier Packet identifier for the subscriptions'\r
+ * SUBSCRIBE packet.\r
+ * @param[in] pSubscriptionList The first element in the array.\r
+ * @param[in] subscriptionCount Number of elements in `pSubscriptionList`.\r
+ *\r
+ * @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.\r
+ */\r
+IotMqttError_t _IotMqtt_AddSubscriptions( _mqttConnection_t * pMqttConnection,\r
+ uint16_t subscribePacketIdentifier,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount );\r
+\r
+/**\r
+ * @brief Process a received PUBLISH from the server, invoking any subscription\r
+ * callbacks that have a matching topic filter.\r
+ *\r
+ * @param[in] pMqttConnection The MQTT connection associated with the received\r
+ * PUBLISH.\r
+ * @param[in] pCallbackParam The parameter to pass to a PUBLISH callback.\r
+ */\r
+void _IotMqtt_InvokeSubscriptionCallback( _mqttConnection_t * pMqttConnection,\r
+ IotMqttCallbackParam_t * pCallbackParam );\r
+\r
+/**\r
+ * @brief Remove a single subscription from the subscription manager by\r
+ * packetIdentifier and order.\r
+ *\r
+ * @param[in] pMqttConnection The MQTT connection associated with the subscriptions.\r
+ * @param[in] packetIdentifier The packet identifier associated with the subscription's\r
+ * SUBSCRIBE packet.\r
+ * @param[in] order The order of the subscription in the SUBSCRIBE packet.\r
+ * Pass `-1` to ignore order and remove all subscriptions for `packetIdentifier`.\r
+ */\r
+void _IotMqtt_RemoveSubscriptionByPacket( _mqttConnection_t * pMqttConnection,\r
+ uint16_t packetIdentifier,\r
+ int32_t order );\r
+\r
+/**\r
+ * @brief Remove an array of subscriptions from the subscription manager by\r
+ * topic filter.\r
+ *\r
+ * @param[in] pMqttConnection The MQTT connection associated with the subscriptions.\r
+ * @param[in] pSubscriptionList The first element in the array.\r
+ * @param[in] subscriptionCount Number of elements in `pSubscriptionList`.\r
+ */\r
+void _IotMqtt_RemoveSubscriptionByTopicFilter( _mqttConnection_t * pMqttConnection,\r
+ const IotMqttSubscription_t * pSubscriptionList,\r
+ size_t subscriptionCount );\r
+\r
+/*------------------ MQTT connection management functions -------------------*/\r
+\r
+/**\r
+ * @brief Attempt to increment the reference count of an MQTT connection.\r
+ *\r
+ * @param[in] pMqttConnection The referenced MQTT connection.\r
+ *\r
+ * @return `true` if the reference count was incremented; `false` otherwise. The\r
+ * reference count will not be incremented for a disconnected connection.\r
+ */\r
+bool _IotMqtt_IncrementConnectionReferences( _mqttConnection_t * pMqttConnection );\r
+\r
+/**\r
+ * @brief Decrement the reference count of an MQTT connection.\r
+ *\r
+ * Also destroys an unreferenced MQTT connection.\r
+ *\r
+ * @param[in] pMqttConnection The referenced MQTT connection.\r
+ */\r
+void _IotMqtt_DecrementConnectionReferences( _mqttConnection_t * pMqttConnection );\r
+\r
+/**\r
+ * @brief Read the next available byte on a network connection.\r
+ *\r
+ * @param[in] pNetworkConnection Reference to the network connection.\r
+ * @param[in] pNetworkInterface Function pointers used to interact with the\r
+ * network.\r
+ * @param[out] pIncomingByte The byte read from the network.\r
+ *\r
+ * @return `true` if a byte was successfully received from the network; `false`\r
+ * otherwise.\r
+ */\r
+bool _IotMqtt_GetNextByte( void * pNetworkConnection,\r
+ const IotNetworkInterface_t * pNetworkInterface,\r
+ uint8_t * pIncomingByte );\r
+\r
+/**\r
+ * @brief Closes the network connection associated with an MQTT connection.\r
+ *\r
+ * A network disconnect function must be set in the network interface for the\r
+ * network connection to be closed.\r
+ *\r
+ * @param[in] disconnectReason A reason to pass to the connection's disconnect\r
+ * callback.\r
+ * @param[in] pMqttConnection The MQTT connection with the network connection\r
+ * to close.\r
+ */\r
+void _IotMqtt_CloseNetworkConnection( IotMqttDisconnectReason_t disconnectReason,\r
+ _mqttConnection_t * pMqttConnection );\r
+\r
+#endif /* ifndef IOT_MQTT_INTERNAL_H_ */\r