Add the Labs projects provided in the V10.2.1_191129 zip file.

This commit is contained in:
Richard Barry 2019-12-02 23:39:25 +00:00
parent 46e5937529
commit e5708b38e9
801 changed files with 356576 additions and 0 deletions

View file

@ -0,0 +1,4 @@
+ platform
Contains FreeRTOS specific implementations of abstractions used within the IoT
libraries.

View file

@ -0,0 +1,173 @@
/*
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file mbedtls_platform.c
* @brief Implements mbed TLS platform functions for FreeRTOS.
*/
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "FreeRTOS_Sockets.h"
/* mbed TLS includes. */
#include "mbedtls_config.h"
#include "threading_alt.h"
#include "mbedtls/entropy.h"
/*-----------------------------------------------------------*/
void * mbedtls_platform_calloc( size_t nmemb,
size_t size )
{
size_t totalSize = nmemb * size;
void * pBuffer = NULL;
/* Check that neither nmemb nor size were 0. */
if( totalSize > 0 )
{
/* Overflow check. */
if( totalSize / size == nmemb )
{
pBuffer = pvPortMalloc( totalSize );
if( pBuffer != NULL )
{
( void ) memset( pBuffer, 0x00, totalSize );
}
}
}
return pBuffer;
}
/*-----------------------------------------------------------*/
void mbedtls_platform_free( void * ptr )
{
vPortFree( ptr );
}
/*-----------------------------------------------------------*/
int mbedtls_platform_send( void * ctx,
const unsigned char * buf,
size_t len )
{
Socket_t socket = ctx;
return ( int ) FreeRTOS_send( socket, buf, len, 0 );
}
/*-----------------------------------------------------------*/
int mbedtls_platform_recv( void * ctx,
unsigned char * buf,
size_t len )
{
Socket_t socket = ctx;
return ( int ) FreeRTOS_recv( socket, buf, len, 0 );
}
/*-----------------------------------------------------------*/
void mbedtls_platform_mutex_init( mbedtls_threading_mutex_t * pMutex )
{
/* Create a statically-allocated FreeRTOS mutex. This should never fail as
* storage is provided. */
pMutex->mutexHandle = xSemaphoreCreateMutexStatic( &( pMutex->mutexStorage ) );
configASSERT( pMutex->mutexHandle != NULL );
}
/*-----------------------------------------------------------*/
void mbedtls_platform_mutex_free( mbedtls_threading_mutex_t * pMutex )
{
/* Nothing needs to be done to free a statically-allocated FreeRTOS mutex. */
( void ) pMutex;
}
/*-----------------------------------------------------------*/
int mbedtls_platform_mutex_lock( mbedtls_threading_mutex_t * pMutex )
{
BaseType_t mutexStatus = 0;
/* mutexStatus is not used if asserts are disabled. */
( void ) mutexStatus;
/* This function should never fail if the mutex is initialized. */
mutexStatus = xSemaphoreTake( pMutex->mutexHandle, portMAX_DELAY );
configASSERT( mutexStatus == pdTRUE );
return 0;
}
/*-----------------------------------------------------------*/
int mbedtls_platform_mutex_unlock( mbedtls_threading_mutex_t * pMutex )
{
BaseType_t mutexStatus = 0;
/* mutexStatus is not used if asserts are disabled. */
( void ) mutexStatus;
/* This function should never fail if the mutex is initialized. */
mutexStatus = xSemaphoreGive( pMutex->mutexHandle );
configASSERT( mutexStatus == pdTRUE );
return 0;
}
/*-----------------------------------------------------------*/
int mbedtls_platform_entropy_poll( void * data,
unsigned char * output,
size_t len,
size_t * olen )
{
int status = 0;
NTSTATUS rngStatus = 0;
/* Context is not used by this function. */
( void ) data;
/* TLS requires a secure random number generator; use the RNG provided
* by Windows. This function MUST be re-implemented for other platforms. */
rngStatus = BCryptGenRandom( NULL, output, len, BCRYPT_USE_SYSTEM_PREFERRED_RNG );
if( rngStatus == 0 )
{
/* All random bytes generated. */
*olen = len;
}
else
{
/* RNG failure. */
*olen = 0;
status = MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
}
return status;
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* mbed TLS threading functions implemented for FreeRTOS. */
#ifndef MBEDTLS_THREADING_ALT_H_
#define MBEDTLS_THREADING_ALT_H_
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "semphr.h"
/* mbed TLS mutex type. */
typedef struct mbedtls_threading_mutex
{
SemaphoreHandle_t mutexHandle;
StaticSemaphore_t mutexStorage;
} mbedtls_threading_mutex_t;
/* mbed TLS mutex functions. */
void mbedtls_platform_mutex_init( mbedtls_threading_mutex_t * pMutex );
void mbedtls_platform_mutex_free( mbedtls_threading_mutex_t * pMutex );
int mbedtls_platform_mutex_lock( mbedtls_threading_mutex_t * pMutex );
int mbedtls_platform_mutex_unlock( mbedtls_threading_mutex_t * pMutex );
#endif

View file

@ -0,0 +1,310 @@
/*
* IoT Common V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_taskpool.h
* @brief User-facing functions of the task pool library.
*/
#ifndef IOT_TASKPOOL_H_
#define IOT_TASKPOOL_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
/* Task pool types. */
#include "types/iot_taskpool_types_freertos.h"
/*------------------------- Task Pool library functions --------------------------*/
/**
* @functionspage{taskpool,task pool library}
* - @functionname{taskpool_function_createsystemtaskpool}
* - @functionname{taskpool_function_createjob}
* - @functionname{taskpool_function_schedule}
* - @functionname{taskpool_function_scheduledeferred}
* - @functionname{taskpool_function_getstatus}
* - @functionname{taskpool_function_trycancel}
* - @functionname{taskpool_function_getjobstoragefromhandle}
* - @functionname{taskpool_function_strerror}
*/
/**
* @functionpage{IotTaskPool_CreateSystemTaskPool,taskpool,createsystemtaskpool}
* @functionpage{IotTaskPool_CreateJob,taskpool,createjob}
* @functionpage{IotTaskPool_Schedule,taskpool,schedule}
* @functionpage{IotTaskPool_ScheduleDeferred,taskpool,scheduledeferred}
* @functionpage{IotTaskPool_GetStatus,taskpool,getstatus}
* @functionpage{IotTaskPool_TryCancel,taskpool,trycancel}
* @functionpage{IotTaskPool_GetJobStorageFromHandle,taskpool,getjobstoragefromhandle}
* @functionpage{IotTaskPool_strerror,taskpool,strerror}
*/
/**
* @brief Creates the one single instance of the system task pool.
*
* This function should be called once by the application to initialize the one single instance of the system task pool.
* An application should initialize the system task pool early in the boot sequence, before initializing any other library
* (e.g. MQTT) that uses the system task pool. An application should also initialize the system
* task pool before posting any jobs. Early initialization is typically easy to accomplish by creating the system task pool
* before the scheduler is started.
*
* The shortcut @ref IOT_SYSTEM_TASKPOOL contains the system task pool handle.
*
* @param[in] pInfo A pointer to the task pool initialization data.
*
* @return One of the following:
* - #IOT_TASKPOOL_SUCCESS
* - #IOT_TASKPOOL_BAD_PARAMETER
* - #IOT_TASKPOOL_NO_MEMORY
*
* @warning This function should be called only once. Calling this function more that once will result in
* undefined behavior.
*
*/
/* @[declare_taskpool_createsystemtaskpool] */
IotTaskPoolError_t IotTaskPool_CreateSystemTaskPool( const IotTaskPoolInfo_t * const pInfo );
/* @[declare_taskpool_createsystemtaskpool] */
/**
* @brief Creates a job for the task pool around a user-provided storage.
*
* @param[in] userCallback A user-specified callback for the job.
* @param[in] pUserContext A user-specified context for the callback.
* @param[in,out] pJobStorage The storage for the job data structure.
* @param[out] pJob A pointer to an instance of @ref IotTaskPoolJob_t that will be initialized when this
* function returns successfully. This handle can be used to inspect the job status with
* @ref IotTaskPool_GetStatus or cancel the job with @ref IotTaskPool_TryCancel, etc....
*
* @return One of the following:
* - #IOT_TASKPOOL_SUCCESS
* - #IOT_TASKPOOL_BAD_PARAMETER
*
*
*/
/* @[declare_taskpool_createjob] */
IotTaskPoolError_t IotTaskPool_CreateJob( IotTaskPoolRoutine_t userCallback,
void * pUserContext,
IotTaskPoolJobStorage_t * const pJobStorage,
IotTaskPoolJob_t * const pJob );
/* @[declare_taskpool_createjob] */
/**
* @brief This function schedules a job created with @ref IotTaskPool_CreateJob against the task pool pointed to by `taskPool`.
*
* @param[in] taskPool A handle to an initialized taskpool.
* @param[in] job A job to schedule for execution. This must be first initialized with a call to @ref IotTaskPool_CreateJob.
* @param[in] flags Flags to be passed by the user, e.g. to identify the job as high priority by specifying #IOT_TASKPOOL_JOB_HIGH_PRIORITY.
*
* @return One of the following:
* - #IOT_TASKPOOL_SUCCESS
* - #IOT_TASKPOOL_BAD_PARAMETER
* - #IOT_TASKPOOL_ILLEGAL_OPERATION
* - #IOT_TASKPOOL_NO_MEMORY
* - #IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS
*
* @note This function will not allocate memory, so it is guaranteed to succeed if the parameters are correct and the task pool
* was correctly initialized, and not yet destroyed.
*
* <b>Example</b>
* @code{c}
* // An example of a user context to pass to a callback through a task pool thread.
* typedef struct JobUserContext
* {
* uint32_t counter;
* } JobUserContext_t;
*
* // An example of a user callback to invoke through a task pool thread.
* static void ExecutionCb( IotTaskPool_t taskPool, IotTaskPoolJob_t job, void * context )
* {
* ( void )taskPool;
* ( void )job;
*
* JobUserContext_t * pUserContext = ( JobUserContext_t * )context;
*
* pUserContext->counter++;
* }
*
* void TaskPoolExample( )
* {
* JobUserContext_t userContext = { 0 };
* IotTaskPoolJob_t job;
* IotTaskPool_t taskPool;
*
* // Configure the task pool to hold one thread.
* // Provide proper stack size and priority per the application needs.
*
* const IotTaskPoolInfo_t tpInfo = { .minThreads = 1, .maxThreads = 1, .stackSize = 512, .priority = 0 };
*
* // Create a task pool.
* IotTaskPool_Create( &tpInfo, &taskPool );
*
* // Statically allocate one job, schedule it.
* IotTaskPool_CreateJob( &ExecutionCb, &userContext, &job );
*
* IotTaskPoolError_t errorSchedule = IotTaskPool_Schedule( taskPool, &job, 0 );
*
* switch ( errorSchedule )
* {
* case IOT_TASKPOOL_SUCCESS:
* break;
* case IOT_TASKPOOL_BAD_PARAMETER: // Invalid parameters, such as a NULL handle, can trigger this error.
* case IOT_TASKPOOL_ILLEGAL_OPERATION: // Scheduling a job that was previously scheduled or destroyed could trigger this error.
* case IOT_TASKPOOL_NO_MEMORY: // Scheduling a with flag #IOT_TASKPOOL_JOB_HIGH_PRIORITY could trigger this error.
* case IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS: // Scheduling a job after trying to destroy the task pool could trigger this error.
* // ASSERT
* break;
* default:
* // ASSERT
* }
*
* //
* // ... Perform other operations ...
* //
*
* IotTaskPool_Destroy( taskPool );
* }
* @endcode
*/
/* @[declare_taskpool_schedule] */
IotTaskPoolError_t IotTaskPool_Schedule( IotTaskPool_t taskPool,
IotTaskPoolJob_t job,
uint32_t flags );
/* @[declare_taskpool_schedule] */
/**
* @brief This function schedules a job created with @ref IotTaskPool_CreateJob to be executed after a user-defined time interval.
*
* @param[in] taskPool A handle to an initialized taskpool.
* @param[in] job A job to schedule for execution. This must be first initialized with a call to @ref IotTaskPool_CreateJob.
* @param[in] timeMs The time in milliseconds to wait before scheduling the job.
*
* @return One of the following:
* - #IOT_TASKPOOL_SUCCESS
* - #IOT_TASKPOOL_BAD_PARAMETER
* - #IOT_TASKPOOL_ILLEGAL_OPERATION
* - #IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS
*
*
* @note This function will not allocate memory.
*
* @warning The `taskPool` used in this function should be the same
* used to create the job pointed to by `job`, or the results will be undefined.
*
*/
/* @[declare_taskpool_scheduledeferred] */
IotTaskPoolError_t IotTaskPool_ScheduleDeferred( IotTaskPool_t taskPool,
IotTaskPoolJob_t job,
uint32_t timeMs );
/* @[declare_taskpool_scheduledeferred] */
/**
* @brief This function retrieves the current status of a job.
*
* @param[in] taskPool A handle to an initialized taskpool.
* @param[in] job The job to cancel.
* @param[out] pStatus The status of the job at the time of cancellation.
*
* @return One of the following:
* - #IOT_TASKPOOL_SUCCESS
* - #IOT_TASKPOOL_BAD_PARAMETER
* - #IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS
*
* @warning This function is not thread safe and the job status returned in `pStatus` may be invalid by the time
* the calling thread has a chance to inspect it.
*/
/* @[declare_taskpool_getstatus] */
IotTaskPoolError_t IotTaskPool_GetStatus( IotTaskPool_t taskPool,
IotTaskPoolJob_t job,
IotTaskPoolJobStatus_t * const pStatus );
/* @[declare_taskpool_getstatus] */
/**
* @brief This function tries to cancel a job that was previously scheduled with @ref IotTaskPool_Schedule.
*
* A job can be canceled only if it is not yet executing, i.e. if its status is
* @ref IOT_TASKPOOL_STATUS_READY or @ref IOT_TASKPOOL_STATUS_SCHEDULED. Calling
* @ref IotTaskPool_TryCancel on a job whose status is @ref IOT_TASKPOOL_STATUS_COMPLETED,
* or #IOT_TASKPOOL_STATUS_CANCELED will yield a #IOT_TASKPOOL_CANCEL_FAILED return result.
*
* @param[in] taskPool A handle to an initialized taskpool.
* @param[in] job The job to cancel.
* @param[out] pStatus The status of the job at the time of cancellation.
*
* @return One of the following:
* - #IOT_TASKPOOL_SUCCESS
* - #IOT_TASKPOOL_BAD_PARAMETER
* - #IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS
* - #IOT_TASKPOOL_CANCEL_FAILED
*
* @warning The `taskPool` used in this function should be the same
* used to create the job pointed to by `job`, or the results will be undefined.
*
*/
/* @[declare_taskpool_trycancel] */
IotTaskPoolError_t IotTaskPool_TryCancel( IotTaskPool_t taskPool,
IotTaskPoolJob_t job,
IotTaskPoolJobStatus_t * const pStatus );
/* @[declare_taskpool_trycancel] */
/**
* @brief Returns a pointer to the job storage from an instance of a job handle
* of type @ref IotTaskPoolJob_t. This function is guaranteed to succeed for a
* valid job handle.
*
* @param[in] job The job handle.
*
* @return A pointer to the storage associated with the job handle `job`.
*
* @warning If the `job` handle used is invalid, the results will be undefined.
*/
/* @[declare_taskpool_getjobstoragefromhandle] */
IotTaskPoolJobStorage_t * IotTaskPool_GetJobStorageFromHandle( IotTaskPoolJob_t job );
/* @[declare_taskpool_getjobstoragefromhandle] */
/**
* @brief Returns a string that describes an @ref IotTaskPoolError_t.
*
* Like the POSIX's `strerror`, this function returns a string describing a
* return code. In this case, the return code is a task pool library error code,
* `status`.
*
* The string returned by this function <b>MUST</b> be treated as read-only: any
* attempt to modify its contents may result in a crash. Therefore, this function
* is limited to usage in logging.
*
* @param[in] status The status to describe.
*
* @return A read-only string that describes `status`.
*
* @warning The string returned by this function must never be modified.
*/
/* @[declare_taskpool_strerror] */
const char * IotTaskPool_strerror( IotTaskPoolError_t status );
/* @[declare_taskpool_strerror] */
#endif /* ifndef IOT_TASKPOOL_H_ */

View file

@ -0,0 +1,150 @@
/*
* Amazon FreeRTOS Platform V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_network_freertos.h
* @brief Declares the network stack functions specified in iot_network.h for
* FreeRTOS+TCP.
*/
#ifndef _IOT_NETWORK_FREERTOS_H_
#define _IOT_NETWORK_FREERTOS_H_
/* Standard includes. */
#include <stdbool.h>
/* Platform types include. */
#include "types/iot_platform_types.h"
/* Platform network include. */
#include "platform/iot_network.h"
/**
* @brief Provides a default value for an #IotNetworkConnectionFreeRTOS_t.
*
* All instances of #IotNetworkConnectionFreeRTOS_t should be initialized with
* this constant.
*
* @warning Failing to initialize an #IotNetworkConnectionFreeRTOS_t with this
* initializer may result in undefined behavior!
* @note This initializer may change at any time in future versions, but its
* name will remain the same.
*/
#define IOT_NETWORK_CONNECTION_FREERTOS_INITIALIZER { 0 }
/**
* @brief Generic initializer for an #IotNetworkServerInfo_t.
*
* @note This initializer may change at any time in future versions, but its
* name will remain the same.
*/
#define IOT_NETWORK_SERVER_INFO_FREERTOS_INITIALIZER { 0 }
/**
* @brief Generic initializer for an #IotNetworkCredentials_t.
*
* @note This initializer may change at any time in future versions, but its
* name will remain the same.
*/
#define IOT_NETWORK_CREDENTIALS_FREERTOS_INITIALIZER { 0 }
/**
* @brief Provides a pointer to an #IotNetworkInterface_t that uses the functions
* declared in this file.
*/
#define IOT_NETWORK_INTERFACE_FREERTOS ( &( IotNetworkFreeRTOS ) )
/**
* @brief One-time initialization function for this network stack.
*/
IotNetworkError_t IotNetworkFreeRTOS_Init( void );
/**
* @brief One-time cleanup function for this network stack.
*/
void IotNetworkFreeRTOS_Cleanup( void );
/**
* @brief An implementation of #IotNetworkInterface_t::create for FreeRTOS+TCP
* sockets.
*/
IotNetworkError_t IotNetworkFreeRTOS_Create( IotNetworkServerInfo_t pServerInfo,
IotNetworkCredentials_t pCredentialInfo,
IotNetworkConnection_t * pConnection );
/**
* @brief An implementation of #IotNetworkInterface_t::setReceiveCallback for
* FreeRTOS+TCP sockets.
*/
IotNetworkError_t IotNetworkFreeRTOS_SetReceiveCallback( IotNetworkConnection_t pConnection,
IotNetworkReceiveCallback_t receiveCallback,
void * pContext );
/**
* @brief An implementation of #IotNetworkInterface_t::send for FreeRTOS+TCP
* sockets.
*/
size_t IotNetworkFreeRTOS_Send( IotNetworkConnection_t pConnection,
const uint8_t * pMessage,
size_t messageLength );
/**
* @brief An implementation of #IotNetworkInterface_t::receive for FreeRTOS+TCP
* sockets.
*/
size_t IotNetworkFreeRTOS_Receive( IotNetworkConnection_t pConnection,
uint8_t * pBuffer,
size_t bytesRequested );
/**
* @brief An implementation of #IotNetworkInterface::receiveUpto for FreeRTOS+TCP
* sockets.
*/
size_t IotNetworkFreeRTOS_ReceiveUpto( IotNetworkConnection_t pConnection,
uint8_t * pBuffer,
size_t bufferSize );
/**
* @brief An implementation of #IotNetworkInterface_t::close for FreeRTOS+TCP
* sockets.
*/
IotNetworkError_t IotNetworkFreeRTOS_Close( IotNetworkConnection_t pConnection );
/**
* @brief An implementation of #IotNetworkInterface_t::destroy for FreeRTOS+TCP
* sockets.
*/
IotNetworkError_t IotNetworkFreeRTOS_Destroy( IotNetworkConnection_t pConnection );
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Declaration of a network interface struct using the functions in this file.
*/
extern const IotNetworkInterface_t IotNetworkFreeRTOS;
/** @endcond */
#endif /* ifndef _IOT_NETWORK_FREERTOS_H_ */

View file

@ -0,0 +1,92 @@
/*
* Amazon FreeRTOS Platform V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_platform_types_posix.h
* @brief Definitions of platform layer types on POSIX systems.
*/
#ifndef _IOT_PLATFORM_TYPES_AFR_H_
#define _IOT_PLATFORM_TYPES_AFR_H_
#include "timers.h"
typedef struct iot_mutex_internal
{
StaticSemaphore_t xMutex; /**< FreeRTOS mutex. */
BaseType_t recursive; /**< Type; used for indicating if this is reentrant or normal. */
} iot_mutex_internal_t;
/**
* @brief The native mutex type on AFR systems.
*/
typedef iot_mutex_internal_t _IotSystemMutex_t;
typedef struct iot_sem_internal
{
StaticSemaphore_t xSemaphore; /**< FreeRTOS semaphore. */
} iot_sem_internal_t;
/**
* @brief The native semaphore type on AFR systems.
*/
typedef iot_sem_internal_t _IotSystemSemaphore_t;
/**
* @brief Holds information about an active detached thread so that we can
* delete the FreeRTOS task when it completes
*/
typedef struct threadInfo
{
void * pArgument; /**< @brief Argument to `threadRoutine`. */
void ( * threadRoutine )( void * ); /**< @brief Thread function to run. */
} threadInfo_t;
/**
* @brief Holds information about an active timer.
*/
typedef struct timerInfo
{
TimerHandle_t timer; /**< @brief Underlying timer. */
void ( * threadRoutine )( void * ); /**< @brief Thread function to run on timer expiration. */
void * pArgument; /**< @brief First argument to threadRoutine. */
StaticTimer_t xTimerBuffer; /**< Memory that holds the FreeRTOS timer. */
TickType_t xTimerPeriod; /**< Period of this timer. */
} timerInfo_t;
/**
* @brief Represents an #IotTimer_t on AFR systems.
*/
typedef timerInfo_t _IotSystemTimer_t;
struct IotNetworkServerInfo;
struct IotNetworkCredentials;
struct _networkConnection;
typedef struct IotNetworkServerInfo const * _IotNetworkServerInfo_t;
typedef struct IotNetworkCredentials * _IotNetworkCredentials_t;
typedef struct _networkConnection * _IotNetworkConnection_t;
#endif /* ifndef _IOT_PLATFORM_TYPES_POSIX_H_ */

View file

@ -0,0 +1,306 @@
/*
* IoT Common V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_taskpool_types.h
* @brief Types of the task pool.
*/
#ifndef IOT_TASKPOOL_TYPES_H_
#define IOT_TASKPOOL_TYPES_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <stdint.h>
/* Platform types includes. */
#include "types/iot_platform_types.h"
/* Linear containers (lists and queues) include. */
#include "iot_linear_containers.h"
/*-------------------------- Task pool enumerated types --------------------------*/
/**
* @ingroup taskpool_datatypes_enums
* @brief Return codes of [task pool functions](@ref taskpool_functions).
*/
typedef enum IotTaskPoolError
{
/**
* @brief Task pool operation completed successfully.
*/
IOT_TASKPOOL_SUCCESS = 0,
/**
* @brief Task pool operation failed because at least one parameter is invalid.
*/
IOT_TASKPOOL_BAD_PARAMETER,
/**
* @brief Task pool operation failed because it is illegal.
*/
IOT_TASKPOOL_ILLEGAL_OPERATION,
/**
* @brief Task pool operation failed because allocating memory failed.
*/
IOT_TASKPOOL_NO_MEMORY,
/**
* @brief Task pool operation failed because of an invalid parameter.
*/
IOT_TASKPOOL_SHUTDOWN_IN_PROGRESS,
/**
* @brief Task pool cancellation failed.
*/
IOT_TASKPOOL_CANCEL_FAILED,
/**
* @brief Task pool operation general failure.
*/
IOT_TASKPOOL_GENERAL_FAILURE,
} IotTaskPoolError_t;
/**
* @enums{taskpool,Task pool library}
*/
/**
* @ingroup taskpool_datatypes_enums
* @brief Status codes of [task pool Job](@ref IotTaskPoolJob_t).
*
*/
typedef enum IotTaskPoolJobStatus
{
/**
* @brief Job is ready to be scheduled.
*
*/
IOT_TASKPOOL_STATUS_READY = 0,
/**
* @brief Job has been queued for execution.
*
*/
IOT_TASKPOOL_STATUS_SCHEDULED,
/**
* @brief Job has been scheduled for deferred execution.
*
*/
IOT_TASKPOOL_STATUS_DEFERRED,
/**
* @brief Job is executing.
*
*/
IOT_TASKPOOL_STATUS_COMPLETED,
/**
* @brief Job has been canceled before executing.
*
*/
IOT_TASKPOOL_STATUS_CANCELED,
/**
* @brief Job status is undefined.
*
*/
IOT_TASKPOOL_STATUS_UNDEFINED,
} IotTaskPoolJobStatus_t;
/*------------------------- Task pool types and handles --------------------------*/
/**
* @ingroup taskpool_datatypes_handles
* @brief Opaque handle of a Task Pool instance.
*
* This type identifies a Task Pool instance, which is valid after a successful call
* to @ref taskpool_function_createsystemtaskpool.
*
* @initializer{IotTaskPool_t,IOT_TASKPOOL_INITIALIZER}
*/
typedef struct _taskPool * IotTaskPool_t;
/**
* @ingroup taskpool_datatypes_handles
* @brief A storage placeholder for TaskPool Jobs.
*
* This type provides a means to statically allocate an IoT TaskPool Job while
* hiding internal details.
*/
typedef struct IotTaskPoolJobStorage IotTaskPoolJobStorage_t;
/**
* @ingroup taskpool_datatypes_structs
* @brief A storage placeholder for private data in the IotTaskPoolJobStorage struct.
*
* @warning This is a system-level data type that should not be modified or used directly in any application.
* @warning This is a system-level data type that can and will change across different versions of the platform, with no regards for backward compatibility.
*/
struct PrivateMember
{
IotLink_t dummy1; /**< @brief Placeholder. */
TickType_t dummy2; /**< @brief Placeholder. */
};
/**
* @ingroup taskpool_datatypes_structs
* @brief The job storage data structure provides the storage for a statically allocated Task Pool Job instance.
*
* @warning This is a system-level data type that should not be modified or used directly in any application.
* @warning This is a system-level data type that can and will change across different versions of the platform, with no regards for backward compatibility.
*
*/
struct IotTaskPoolJobStorage
{
IotLink_t link; /**< @brief Placeholder. */
void * dummy2; /**< @brief Placeholder. */
void * dummy3; /**< @brief Placeholder. */
IotTaskPoolJobStatus_t status; /**< @brief Placeholder. */
struct PrivateMember dummy6; /**< @brief Placeholder. */
};
/**
* @ingroup taskpool_datatypes_handles
* @brief Opaque handle of a Task Pool Job.
*
* This type identifies a Task Pool Job instance, which is valid after a successful call
* to @ref taskpool_function_createjob.
*
* @initializer{IotTaskPoolJob_t,IOT_TASKPOOL_JOB_INITIALIZER}
*
*/
typedef struct _taskPoolJob * IotTaskPoolJob_t;
/*------------------------- Task pool parameter structs --------------------------*/
/**
* @ingroup taskpool_datatypes_functionpointers
* @brief Callback type for a user callback.
*
* This type identifies the user callback signature to execute a task pool job. This callback will be invoked
* by the task pool threads with the `pUserContext` parameter, as specified by the user when
* calling @ref IotTaskPool_Schedule.
*
*/
typedef void ( * IotTaskPoolRoutine_t )( IotTaskPool_t pTaskPool,
IotTaskPoolJob_t pJob,
void * pUserContext );
/**
* @ingroup taskpool_datatypes_paramstructs
* @brief Initialization information to create one task pool instance.
*
* @paramfor @ref taskpool_function_createsystemtaskpool
*
* @initializer{IotTaskPoolInfo_t,IOT_TASKPOOL_INFO_INITIALIZER}
*/
typedef struct IotTaskPoolInfo
{
/**
* @brief Specifies the operating parameters for a task pool.
*
* @attention #IotTaskPoolInfo_t.minThreads <b>MUST</b> be at least 1.
*/
uint32_t minThreads; /**< @brief Minimum number of threads in a task pool. These tasks will be statically allocated. */
uint32_t maxThreads; /**< @brief Maximum number of threads in a task pool. This is fixed for the lifetime of the taskpool. */
uint32_t stackSize; /**< @brief Stack size for every task pool thread. The stack size for each thread is fixed after the task pool is created and cannot be changed. */
int32_t priority; /**< @brief priority for every task pool thread. The priority for each thread is fixed after the task pool is created and cannot be changed. */
} IotTaskPoolInfo_t;
/*------------------------- TASKPOOL defined constants --------------------------*/
/**
* @constantspage{taskpool,task pool library}
*
* @section taskpool_constants_initializers Task pool Initializers
* @brief Provides default values for initializing the data types of the task pool library.
*
* @snippet this define_taskpool_initializers
*
* All user-facing data types of the task pool library can be initialized using
* one of the following.
*
* @warning Failure to initialize a task pool data type with the appropriate initializer
* may result in a runtime error!
* @note The initializers may change at any time in future versions, but their
* names will remain the same.
*
* <b>Example</b>
* @code{c}
*
* IotTaskPool_t * pTaskPool;
*
* const IotTaskPoolInfo_t tpInfo = IOT_TASKPOOL_INFO_INITIALIZER_LARGE;
*
* IotTaskPoolError_t error = IotTaskPool_Create( &tpInfo, &pTaskPool );
*
* // Use the task pool
* // ...
*
* @endcode
*
*/
/* @[define_taskpool_initializers] */
/** @brief Initializer for a small #IotTaskPoolInfo_t. */
#define IOT_TASKPOOL_INFO_INITIALIZER_SMALL { .minThreads = 1, .maxThreads = 1, .stackSize = IOT_THREAD_DEFAULT_STACK_SIZE, .priority = IOT_THREAD_DEFAULT_PRIORITY }
/** @brief Initializer for a medium #IotTaskPoolInfo_t. */
#define IOT_TASKPOOL_INFO_INITIALIZER_MEDIUM { .minThreads = 1, .maxThreads = 2, .stackSize = IOT_THREAD_DEFAULT_STACK_SIZE, .priority = IOT_THREAD_DEFAULT_PRIORITY }
/** @brief Initializer for a large #IotTaskPoolInfo_t. */
#define IOT_TASKPOOL_INFO_INITIALIZER_LARGE { .minThreads = 2, .maxThreads = 3, .stackSize = IOT_THREAD_DEFAULT_STACK_SIZE, .priority = IOT_THREAD_DEFAULT_PRIORITY }
/** @brief Initializer for a very large #IotTaskPoolInfo_t. */
#define IOT_TASKPOOL_INFO_INITIALIZER_XLARGE { .minThreads = 2, .maxThreads = 4, .stackSize = IOT_THREAD_DEFAULT_STACK_SIZE, .priority = IOT_THREAD_DEFAULT_PRIORITY }
/** @brief Initializer for a typical #IotTaskPoolInfo_t. */
#define IOT_TASKPOOL_INFO_INITIALIZER IOT_TASKPOOL_INFO_INITIALIZER_MEDIUM
/** @brief Initializer for a #IotTaskPool_t. */
#define IOT_TASKPOOL_INITIALIZER NULL
/** @brief Initializer for a #IotTaskPoolJobStorage_t. */
#define IOT_TASKPOOL_JOB_STORAGE_INITIALIZER { { NULL, NULL }, NULL, NULL, 0, IOT_TASKPOOL_STATUS_UNDEFINED }
/** @brief Initializer for a #IotTaskPoolJob_t. */
#define IOT_TASKPOOL_JOB_INITIALIZER NULL
/* @[define_taskpool_initializers] */
/**
* @brief Flag for scheduling a job to execute immediately, even if the maximum number of threads in the
* task pool was reached already.
*
* @warning This flag may cause the task pool to create a worker to serve the job immediately, and
* therefore using this flag may incur in additional memory usage and potentially fail scheduling the job.
*/
#define IOT_TASKPOOL_JOB_HIGH_PRIORITY ( ( uint32_t ) 0x00000001 )
/**
* @brief Allows the use of the handle to the system task pool.
*
* @warning The system task pool handle is not valid unless @ref IotTaskPool_CreateSystemTaskPool is
* called before the handle is used.
*/
#define IOT_SYSTEM_TASKPOOL ( NULL )
#endif /* ifndef IOT_TASKPOOL_TYPES_H_ */

View file

@ -0,0 +1,225 @@
/*
* Amazon FreeRTOS Platform V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_clock_freertos.c
* @brief Implementation of the platform specific functions in iot_clock.h for
* FreeRTOS.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdio.h>
/* Platform clock include. */
#include "platform/iot_platform_types_freertos.h"
#include "platform/iot_clock.h"
#include "task.h"
/* Configure logs for the functions in this file. */
#ifdef IOT_LOG_LEVEL_PLATFORM
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_PLATFORM
#else
#ifdef IOT_LOG_LEVEL_GLOBAL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
#else
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
#endif
#endif
#define LIBRARY_LOG_NAME ( "CLOCK" )
#include "iot_logging_setup.h"
/*-----------------------------------------------------------*/
/*
* Time conversion constants.
*/
#define _MILLISECONDS_PER_SECOND ( 1000 ) /**< @brief Milliseconds per second. */
#define _MILLISECONDS_PER_TICK ( _MILLISECONDS_PER_SECOND / configTICK_RATE_HZ ) /**< Milliseconds per FreeRTOS tick. */
/*-----------------------------------------------------------*/
/* Private Callback function for timer expiry, delegate work to a Task to free
* up the timer task for managing other timers */
static void prvTimerCallback( TimerHandle_t xTimerHandle )
{
_IotSystemTimer_t * pxTimer = ( _IotSystemTimer_t * ) pvTimerGetTimerID( xTimerHandle );
/* The value of the timer ID, set in timer_create, should not be NULL. */
configASSERT( pxTimer != NULL );
/* Restart the timer if it is periodic. */
if( pxTimer->xTimerPeriod > 0 )
{
xTimerChangePeriod( xTimerHandle, pxTimer->xTimerPeriod, 0 );
}
/* Call timer Callback from this task */
pxTimer->threadRoutine( ( void * ) pxTimer->pArgument );
}
/*-----------------------------------------------------------*/
bool IotClock_GetTimestring( char * pBuffer,
size_t bufferSize,
size_t * pTimestringLength )
{
uint64_t milliSeconds = IotClock_GetTimeMs();
int timestringLength = 0;
configASSERT( pBuffer != NULL );
configASSERT( pTimestringLength != NULL );
/* Convert the localTime struct to a string. */
timestringLength = snprintf( pBuffer, bufferSize, "%llu", milliSeconds );
/* Check for error from no string */
if( timestringLength == 0 )
{
return false;
}
/* Set the output parameter. */
*pTimestringLength = timestringLength;
return true;
}
/*-----------------------------------------------------------*/
uint64_t IotClock_GetTimeMs( void )
{
TimeOut_t xCurrentTime = { 0 };
/* This must be unsigned because the behavior of signed integer overflow is undefined. */
uint64_t ullTickCount = 0ULL;
/* Get the current tick count and overflow count. vTaskSetTimeOutState()
* is used to get these values because they are both static in tasks.c. */
vTaskSetTimeOutState( &xCurrentTime );
/* Adjust the tick count for the number of times a TickType_t has overflowed. */
ullTickCount = ( uint64_t ) ( xCurrentTime.xOverflowCount ) << ( sizeof( TickType_t ) * 8 );
/* Add the current tick count. */
ullTickCount += xCurrentTime.xTimeOnEntering;
/* Return the ticks converted to Milliseconds */
return ullTickCount * _MILLISECONDS_PER_TICK;
}
/*-----------------------------------------------------------*/
void IotClock_SleepMs( uint32_t sleepTimeMs )
{
vTaskDelay( pdMS_TO_TICKS( sleepTimeMs ) );
}
/*-----------------------------------------------------------*/
bool IotClock_TimerCreate( IotTimer_t * pNewTimer,
IotThreadRoutine_t expirationRoutine,
void * pArgument )
{
_IotSystemTimer_t * pxTimer = ( _IotSystemTimer_t * ) pNewTimer;
configASSERT( pNewTimer != NULL );
configASSERT( expirationRoutine != NULL );
IotLogDebug( "Creating new timer %p.", pNewTimer );
/* Set the timer expiration routine, argument and period */
pxTimer->threadRoutine = expirationRoutine;
pxTimer->pArgument = pArgument;
pxTimer->xTimerPeriod = 0;
/* Create a new FreeRTOS timer. This call will not fail because the
* memory for it has already been allocated, so the output parameter is
* also set. */
pxTimer->timer = ( TimerHandle_t ) xTimerCreateStatic( "timer", /* Timer name. */
portMAX_DELAY, /* Initial timer period. Timers are created disarmed. */
pdFALSE, /* Don't auto-reload timer. */
( void * ) pxTimer, /* Timer id. */
prvTimerCallback, /* Timer expiration callback. */
&pxTimer->xTimerBuffer ); /* Pre-allocated memory for timer. */
return true;
}
/*-----------------------------------------------------------*/
void IotClock_TimerDestroy( IotTimer_t * pTimer )
{
_IotSystemTimer_t * pTimerInfo = ( _IotSystemTimer_t * ) pTimer;
configASSERT( pTimerInfo != NULL );
configASSERT( pTimerInfo->timer != NULL );
IotLogDebug( "Destroying timer %p.", pTimer );
if( xTimerIsTimerActive( pTimerInfo->timer ) == pdTRUE )
{
/* Stop the FreeRTOS timer. Because the timer is statically allocated, no call
* to xTimerDelete is necessary. The timer is stopped so that it's not referenced
* anywhere. xTimerStop will not fail when it has unlimited block time. */
( void ) xTimerStop( pTimerInfo->timer, portMAX_DELAY );
/* Wait until the timer stop command is processed. */
while( xTimerIsTimerActive( pTimerInfo->timer ) == pdTRUE )
{
vTaskDelay( 1 );
}
}
}
/*-----------------------------------------------------------*/
bool IotClock_TimerArm( IotTimer_t * pTimer,
uint32_t relativeTimeoutMs,
uint32_t periodMs )
{
_IotSystemTimer_t * pTimerInfo = ( _IotSystemTimer_t * ) pTimer;
configASSERT( pTimerInfo != NULL );
TimerHandle_t xTimerHandle = pTimerInfo->timer;
IotLogDebug( "Arming timer %p with timeout %llu and period %llu.",
pTimer,
relativeTimeoutMs,
periodMs );
/* Set the timer period in ticks */
pTimerInfo->xTimerPeriod = pdMS_TO_TICKS( periodMs );
/* Set the timer to expire after relativeTimeoutMs, and restart it. */
( void ) xTimerChangePeriod( xTimerHandle, pdMS_TO_TICKS( relativeTimeoutMs ), portMAX_DELAY );
return true;
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,968 @@
/*
* Amazon FreeRTOS Platform V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_network_freertos.c
* @brief Implementation of the network-related functions from iot_network_freertos.h
* for FreeRTOS+TCP sockets.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "atomic.h"
#include "semphr.h"
/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
/* FreeRTOS-IoT-Libraries includes. */
#include "iot_error.h"
#include "platform/iot_network_freertos.h"
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/* mbed TLS includes. */
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ssl.h"
#include "mbedtls/threading.h"
#include "mbedtls/x509.h"
#endif
/* Configure logs for the functions in this file. */
#ifdef IOT_LOG_LEVEL_NETWORK
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_NETWORK
#else
#ifdef IOT_LOG_LEVEL_GLOBAL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
#else
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
#endif
#endif
#define LIBRARY_LOG_NAME ( "NET" )
#include "iot_logging_setup.h"
/* Provide a default value for socket timeout and network task parameters. */
#ifndef IOT_NETWORK_SOCKET_TIMEOUT_MS
#define IOT_NETWORK_SOCKET_TIMEOUT_MS ( 5000 )
#endif
#ifndef IOT_NETWORK_TASK_STACK_SIZE
#define IOT_NETWORK_TASK_STACK_SIZE ( 2048 )
#endif
#ifndef IOT_NETWORK_TASK_PRIORITY
#define IOT_NETWORK_TASK_PRIORITY ( tskIDLE_PRIORITY )
#endif
/* Maximum number of simultaneous socket receive callbacks. */
#ifndef IOT_NETWORK_MAX_RECEIVE_CALLBACKS
#define IOT_NETWORK_MAX_RECEIVE_CALLBACKS ( 2 )
#endif
/**
* @brief Maximum length of a DNS name.
*
* Per https://tools.ietf.org/html/rfc1035, 253 is the maximum string length
* of a DNS name.
*/
#define MAX_DNS_NAME_LENGTH ( 253 )
/*-----------------------------------------------------------*/
/**
* @brief Internal network context.
*/
typedef struct _networkConnection
{
Socket_t socket; /**< @brief FreeRTOS+TCP sockets handle. */
SemaphoreHandle_t socketMutex; /**< @brief Prevents concurrent threads from using a socket. */
StaticSemaphore_t socketMutexStorage; /**< @brief Storage space for socketMutex. */
IotNetworkReceiveCallback_t receiveCallback; /**< @brief Network receive callback, if any. */
void * pReceiveContext; /**< @brief The context for the receive callback. */
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
BaseType_t secured; /**< @brief Flag that marks a connection as secured. */
/**
* @brief Secured connection context. Valid if `secured` is `pdTRUE`.
*/
struct
{
mbedtls_ssl_config config; /**< @brief SSL connection configuration. */
mbedtls_ssl_context context; /**< @brief SSL connection context */
mbedtls_x509_crt_profile certProfile; /**< @brief Certificate security profile for this connection. */
mbedtls_x509_crt rootCa; /**< @brief Root CA certificate context. */
mbedtls_x509_crt clientCert; /**< @brief Client certificate context. */
mbedtls_pk_context privKey; /**< @brief Client private key context. */
} ssl;
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
} _networkConnection_t;
/*-----------------------------------------------------------*/
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/**
* @brief mbed TLS entropy context for generation of random numbers.
*/
static mbedtls_entropy_context _entropyContext;
/**
* @brief mbed TLS CTR DRBG context for generation of random numbers.
*/
static mbedtls_ctr_drbg_context _ctrDrgbContext;
#endif
/**
* @brief Handle of the network task.
*/
static TaskHandle_t _networkTaskHandle;
/**
* @brief Socket set for the network task.
*/
static SocketSet_t _socketSet;
/**
* @brief Connections in _socketSet.
*/
static _networkConnection_t * _connections[ IOT_NETWORK_MAX_RECEIVE_CALLBACKS ];
/**
* @brief An #IotNetworkInterface_t that uses the functions in this file.
*/
const IotNetworkInterface_t IotNetworkFreeRTOS =
{
.create = IotNetworkFreeRTOS_Create,
.setReceiveCallback = IotNetworkFreeRTOS_SetReceiveCallback,
.send = IotNetworkFreeRTOS_Send,
.receive = IotNetworkFreeRTOS_Receive,
.receiveUpto = IotNetworkFreeRTOS_ReceiveUpto,
.close = IotNetworkFreeRTOS_Close,
.destroy = IotNetworkFreeRTOS_Destroy
};
/*-----------------------------------------------------------*/
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/**
* @brief Initialize the mbed TLS structures in a network connection.
*
* @param[in] pNetworkConnection The network connection to initialize.
*/
static void _sslContextInit( _networkConnection_t * pNetworkConnection )
{
mbedtls_ssl_config_init( &( pNetworkConnection->ssl.config ) );
mbedtls_x509_crt_init( &( pNetworkConnection->ssl.rootCa ) );
mbedtls_pk_init( &( pNetworkConnection->ssl.privKey ) );
mbedtls_x509_crt_init( &( pNetworkConnection->ssl.clientCert ) );
mbedtls_ssl_init( &( pNetworkConnection->ssl.context ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Free the mbed TLS structures in a network connection.
*
* @param[in] pNetworkConnection The network connection with the contexts to free.
*/
static void _sslContextFree( _networkConnection_t * pNetworkConnection )
{
mbedtls_ssl_free( &( pNetworkConnection->ssl.context ) );
mbedtls_x509_crt_free( &( pNetworkConnection->ssl.rootCa ) );
mbedtls_x509_crt_free( &( pNetworkConnection->ssl.clientCert ) );
mbedtls_pk_free( &( pNetworkConnection->ssl.privKey ) );
mbedtls_ssl_config_free( &( pNetworkConnection->ssl.config ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Set up TLS on a TCP connection.
*
* @param[in] pNetworkConnection An established TCP connection.
* @param[in] pServerName Remote host name, used for server name indication.
* @param[in] pCredentials TLS setup parameters.
*
* @return #IOT_NETWORK_SUCCESS, #IOT_NETWORK_FAILURE, #IOT_NETWORK_NO_MEMORY,
* or #IOT_NETWORK_SYSTEM_ERROR.
*/
static IotNetworkError_t _tlsSetup( _networkConnection_t * pNetworkConnection,
const char * pServerName,
IotNetworkCredentials_t pCredentials )
{
IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );
int mbedtlsError = 0;
/* Initialize the mbed TLS context structures. */
_sslContextInit( pNetworkConnection );
mbedtlsError = mbedtls_ssl_config_defaults( &( pNetworkConnection->ssl.config ),
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to set default SSL configuration, error %d.", mbedtlsError );
/* Per mbed TLS docs, mbedtls_ssl_config_defaults only fails on memory allocation. */
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_NO_MEMORY );
}
/* Set up the certificate security profile, starting from the default value. */
pNetworkConnection->ssl.certProfile = mbedtls_x509_crt_profile_default;
/* test.mosquitto.org only provides a 1024-bit RSA certificate, which is
* not acceptable by the default mbed TLS certificate security profile.
* For the purposes of this demo, allow the use of 1024-bit RSA certificates.
* This block should be removed otherwise. */
if( strncmp( pServerName, "test.mosquitto.org", strlen( pServerName ) ) == 0 )
{
pNetworkConnection->ssl.certProfile.rsa_min_bitlen = 1024;
}
/* Set SSL authmode and the RNG context. */
mbedtls_ssl_conf_authmode( &( pNetworkConnection->ssl.config ),
MBEDTLS_SSL_VERIFY_REQUIRED );
mbedtls_ssl_conf_rng( &( pNetworkConnection->ssl.config ),
mbedtls_ctr_drbg_random,
&_ctrDrgbContext );
mbedtls_ssl_conf_cert_profile( &( pNetworkConnection->ssl.config ),
&( pNetworkConnection->ssl.certProfile ) );
/* Parse the server root CA certificate into the SSL context. */
mbedtlsError = mbedtls_x509_crt_parse( &( pNetworkConnection->ssl.rootCa ),
( const unsigned char * ) pCredentials->pRootCa,
pCredentials->rootCaSize );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to parse server root CA certificate, error %d.",
mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
mbedtls_ssl_conf_ca_chain( &( pNetworkConnection->ssl.config ),
&( pNetworkConnection->ssl.rootCa ),
NULL );
if( ( pCredentials->pPrivateKey != NULL ) && ( pCredentials->pClientCert != NULL ) )
{
/* Setup the client private key. */
mbedtlsError = mbedtls_pk_parse_key( &( pNetworkConnection->ssl.privKey ),
( const unsigned char * ) pCredentials->pPrivateKey,
pCredentials->privateKeySize,
0,
0 );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to parse client certificate, error %d.",
mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
/* Setup the client certificate. */
mbedtlsError = mbedtls_x509_crt_parse( &( pNetworkConnection->ssl.clientCert ),
( const unsigned char * ) pCredentials->pClientCert,
pCredentials->clientCertSize );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to parse the client private key, error %d.",
mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
mbedtls_ssl_conf_own_cert( &( pNetworkConnection->ssl.config ),
&( pNetworkConnection->ssl.clientCert ),
&( pNetworkConnection->ssl.privKey ) );
}
/* Initialize the mbed TLS secured connection context. */
mbedtlsError = mbedtls_ssl_setup( &( pNetworkConnection->ssl.context ),
&( pNetworkConnection->ssl.config ) );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to set up mbed TLS SSL context, error %d.",
mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
/* Set the underlying IO for the TLS connection. */
mbedtls_ssl_set_bio( &( pNetworkConnection->ssl.context ),
pNetworkConnection->socket,
mbedtls_platform_send,
mbedtls_platform_recv,
NULL );
/* Enable SNI if requested. */
if( pCredentials->disableSni == false )
{
mbedtlsError = mbedtls_ssl_set_hostname( &( pNetworkConnection->ssl.context ),
pServerName );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to set server name, error %d.", mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
}
/* Perform the TLS handshake. */
do
{
mbedtlsError = mbedtls_ssl_handshake( &( pNetworkConnection->ssl.context ) );
} while( ( mbedtlsError == MBEDTLS_ERR_SSL_WANT_READ ) ||
( mbedtlsError == MBEDTLS_ERR_SSL_WANT_WRITE ) );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to perform TLS handshake, error %d.", mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );
}
/* Clean up on error. */
IOT_FUNCTION_CLEANUP_BEGIN();
if( status != IOT_NETWORK_SUCCESS )
{
_sslContextFree( pNetworkConnection );
}
else
{
pNetworkConnection->secured = pdTRUE;
IotLogInfo( "(Network connection %p) TLS handshake successful.",
pNetworkConnection );
}
IOT_FUNCTION_CLEANUP_END();
}
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
/*-----------------------------------------------------------*/
static void _networkTask( void * pvParameters )
{
_networkConnection_t * pConnection = NULL;
BaseType_t socketEvents = 0, i = 0, socketStatus = 0;
SocketSet_t socketSet = pvParameters;
while( true )
{
socketEvents = FreeRTOS_select( socketSet, IOT_NETWORK_SOCKET_TIMEOUT_MS );
if( socketEvents > 0 )
{
for( i = 0; i < IOT_NETWORK_MAX_RECEIVE_CALLBACKS; i++ )
{
pConnection = _connections[ i ];
if( pConnection != NULL )
{
socketStatus = FreeRTOS_FD_ISSET( pConnection->socket, socketSet );
if( socketStatus & eSELECT_READ )
{
/* A receive callback must be set; otherwise, select should not
* have returned this socket. */
configASSERT( pConnection->receiveCallback != NULL );
pConnection->receiveCallback( pConnection,
pConnection->pReceiveContext );
}
}
}
}
/* This task will receive a notification when cleanup is called. Exit when
* cleanup is called. */
if( ulTaskNotifyTake( pdTRUE, 0 ) != 0 )
{
break;
}
}
FreeRTOS_DeleteSocketSet( socketSet );
vTaskDelete( NULL );
}
/*-----------------------------------------------------------*/
IotNetworkError_t IotNetworkFreeRTOS_Init( void )
{
IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
int mbedtlsError = 0;
/* Set the mutex functions for mbed TLS thread safety. */
mbedtls_threading_set_alt( mbedtls_platform_mutex_init,
mbedtls_platform_mutex_free,
mbedtls_platform_mutex_lock,
mbedtls_platform_mutex_unlock );
/* Initialize contexts for random number generation. */
mbedtls_entropy_init( &_entropyContext );
mbedtls_ctr_drbg_init( &_ctrDrgbContext );
/* Add a strong entropy source. At least one is required. */
mbedtlsError = mbedtls_entropy_add_source( &_entropyContext,
mbedtls_platform_entropy_poll,
NULL,
32,
MBEDTLS_ENTROPY_SOURCE_STRONG );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to add entropy source, error %d.", mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );
}
/* Seed the random number generator. */
mbedtlsError = mbedtls_ctr_drbg_seed( &_ctrDrgbContext,
mbedtls_entropy_func,
&_entropyContext,
NULL,
0 );
if( mbedtlsError != 0 )
{
IotLogError( "Failed to seed PRNG, error %d.", mbedtlsError );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );
}
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
/* Create socket set for network task. */
_socketSet = FreeRTOS_CreateSocketSet();
if( _socketSet == NULL )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_FAILURE );
}
static StaticTask_t networkTask;
static StackType_t networkTaskStack[ IOT_NETWORK_TASK_STACK_SIZE ];
/* Create the network task. Since valid parameters are provided, this should
* never fail. */
_networkTaskHandle = xTaskCreateStatic( _networkTask,
"Network",
IOT_NETWORK_TASK_STACK_SIZE,
_socketSet,
IOT_NETWORK_TASK_PRIORITY,
( StackType_t * const ) &networkTaskStack,
&networkTask );
configASSERT( _networkTaskHandle != NULL );
IotLogInfo( "Network successfully initialized." );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
void IotNetworkFreeRTOS_Cleanup( void )
{
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/* Free the contexts for random number generation. */
mbedtls_ctr_drbg_free( &_ctrDrgbContext );
mbedtls_entropy_free( &_entropyContext );
/* Clear the mutex functions for mbed TLS thread safety. */
mbedtls_threading_free_alt();
#endif
xTaskNotifyGive( _networkTaskHandle );
IotLogInfo( "Network cleanup done." );
}
/*-----------------------------------------------------------*/
IotNetworkError_t IotNetworkFreeRTOS_Create( IotNetworkServerInfo_t pServerInfo,
IotNetworkCredentials_t pCredentialInfo,
IotNetworkConnection_t * pConnection )
{
IOT_FUNCTION_ENTRY( IotNetworkError_t, IOT_NETWORK_SUCCESS );
Socket_t tcpSocket = FREERTOS_INVALID_SOCKET;
BaseType_t socketStatus = 0;
struct freertos_sockaddr serverAddress = { 0 };
const TickType_t receiveTimeout = pdMS_TO_TICKS( IOT_NETWORK_SOCKET_TIMEOUT_MS );
_networkConnection_t * pNewNetworkConnection = NULL;
/* Credentials are not used if TLS is disabled. */
( void ) pCredentialInfo;
/* Check host name length against the maximum length allowed. */
const size_t hostnameLength = strlen( pServerInfo->pHostName );
if( hostnameLength > ( size_t ) MAX_DNS_NAME_LENGTH )
{
IotLogError( "Host name length exceeds %d, which is the maximum allowed.",
MAX_DNS_NAME_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_BAD_PARAMETER );
}
pNewNetworkConnection = pvPortMalloc( sizeof( _networkConnection_t ) );
if( pNewNetworkConnection == NULL )
{
IotLogError( "Failed to allocate memory for new network connection." );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_NO_MEMORY );
}
/* Clear the connection information. */
( void ) memset( pNewNetworkConnection, 0x00, sizeof( _networkConnection_t ) );
/* Create a new TCP socket. */
tcpSocket = FreeRTOS_socket( FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP );
if( tcpSocket == FREERTOS_INVALID_SOCKET )
{
IotLogError( "Failed to create new socket." );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
/* Set the timeout for receive. */
socketStatus = FreeRTOS_setsockopt( tcpSocket,
0,
FREERTOS_SO_RCVTIMEO,
&receiveTimeout,
sizeof( TickType_t ) );
if( socketStatus != 0 )
{
IotLogError( "Failed to set socket receive timeout." );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
/* Establish connection. */
serverAddress.sin_family = FREERTOS_AF_INET;
serverAddress.sin_port = FreeRTOS_htons( pServerInfo->port );
serverAddress.sin_addr = FreeRTOS_gethostbyname( pServerInfo->pHostName );
serverAddress.sin_len = ( uint8_t ) sizeof( serverAddress );
/* Check for errors from DNS lookup. */
if( serverAddress.sin_addr == 0 )
{
IotLogError( "Failed to resolve %s.", pServerInfo->pHostName );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
socketStatus = FreeRTOS_connect( tcpSocket,
&serverAddress,
sizeof( serverAddress ) );
if( socketStatus != 0 )
{
IotLogError( "Failed to establish new connection. Socket status %d.", socketStatus );
IOT_SET_AND_GOTO_CLEANUP( IOT_NETWORK_SYSTEM_ERROR );
}
/* Set the socket. */
pNewNetworkConnection->socket = tcpSocket;
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/* Set up TLS if credentials are provided. */
if( pCredentialInfo != NULL )
{
status = _tlsSetup( pNewNetworkConnection,
pServerInfo->pHostName,
pCredentialInfo );
}
#endif
IOT_FUNCTION_CLEANUP_BEGIN();
/* Clean up on failure. */
if( status != IOT_NETWORK_SUCCESS )
{
if( tcpSocket != FREERTOS_INVALID_SOCKET )
{
FreeRTOS_closesocket( tcpSocket );
}
/* Clear the connection information. */
if( pNewNetworkConnection != NULL )
{
vPortFree( pNewNetworkConnection );
}
}
else
{
/* Create the socket mutex. */
pNewNetworkConnection->socketMutex = xSemaphoreCreateMutexStatic( &( pNewNetworkConnection->socketMutexStorage ) );
/* Set the output parameter. */
*pConnection = pNewNetworkConnection;
IotLogInfo( "(Network connection %p) Connection to %s established.",
pNewNetworkConnection,
pServerInfo->pHostName );
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/
IotNetworkError_t IotNetworkFreeRTOS_SetReceiveCallback( IotNetworkConnection_t pConnection,
IotNetworkReceiveCallback_t receiveCallback,
void * pContext )
{
IotNetworkError_t status = IOT_NETWORK_SUCCESS;
BaseType_t i = 0;
/* Set the receive callback and context. */
pConnection->receiveCallback = receiveCallback;
pConnection->pReceiveContext = pContext;
/* Add this connection to the list of connections that select should check. */
for( i = 0; i < IOT_NETWORK_MAX_RECEIVE_CALLBACKS; i++ )
{
if( Atomic_CompareAndSwapPointers_p32( &_connections[ i ],
pConnection,
NULL ) == 1 )
{
break;
}
}
if( i == IOT_NETWORK_MAX_RECEIVE_CALLBACKS )
{
status = IOT_NETWORK_NO_MEMORY;
}
else
{
/* Add this socket to the socket set for the network task. */
FreeRTOS_FD_SET( pConnection->socket,
_socketSet,
eSELECT_READ );
}
return status;
}
/*-----------------------------------------------------------*/
size_t IotNetworkFreeRTOS_Send( IotNetworkConnection_t pConnection,
const uint8_t * pMessage,
size_t messageLength )
{
size_t bytesSent = 0;
BaseType_t socketStatus = 0;
/* Only one thread at a time may send on the connection. Lock the send
* mutex to prevent other threads from sending. */
if( xSemaphoreTake( pConnection->socketMutex,
IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )
{
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
if( pConnection->secured == pdTRUE )
{
while( bytesSent < messageLength )
{
socketStatus = ( BaseType_t ) mbedtls_ssl_write( &( pConnection->ssl.context ),
pMessage + bytesSent,
messageLength - bytesSent );
if( ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) ||
( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) )
{
/* Try again for WANT_WRITE and WANT_READ errors. */
continue;
}
else if( socketStatus < 0 )
{
/* Exit on other errors. */
break;
}
else
{
bytesSent += ( size_t ) socketStatus;
configASSERT( bytesSent <= messageLength );
}
}
}
else
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
{
socketStatus = FreeRTOS_send( pConnection->socket,
pMessage,
messageLength,
0 );
}
if( socketStatus > 0 )
{
bytesSent = ( size_t ) socketStatus;
}
xSemaphoreGive( pConnection->socketMutex );
}
IotLogDebug( "(Network connection %p) Sent %lu bytes.",
pConnection,
( unsigned long ) bytesSent );
return bytesSent;
}
/*-----------------------------------------------------------*/
size_t IotNetworkFreeRTOS_Receive( IotNetworkConnection_t pConnection,
uint8_t * pBuffer,
size_t bytesRequested )
{
BaseType_t socketStatus = 0;
size_t bytesReceived = 0, bytesRemaining = bytesRequested;
/* Block and wait for incoming data. */
while( bytesRemaining > 0 )
{
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
if( pConnection->secured == pdTRUE )
{
if( xSemaphoreTake( pConnection->socketMutex,
IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )
{
socketStatus = ( BaseType_t ) mbedtls_ssl_read( &( pConnection->ssl.context ),
pBuffer + bytesReceived,
bytesRequested - bytesReceived );
xSemaphoreGive( pConnection->socketMutex );
if( ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) ||
( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) )
{
/* Try again for WANT_WRITE and WANT_READ errors. */
continue;
}
}
else
{
/* Could not obtain socket mutex, exit. */
break;
}
}
else
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
{
socketStatus = FreeRTOS_recv( pConnection->socket,
pBuffer + bytesReceived,
bytesRemaining,
0 );
if( socketStatus == FREERTOS_EWOULDBLOCK )
{
/* The return value EWOULDBLOCK means no data was received within
* the socket timeout. Ignore it and try again. */
continue;
}
}
if( socketStatus < 0 )
{
IotLogError( "Error %ld while receiving data.", ( long int ) socketStatus );
break;
}
else
{
bytesReceived += ( size_t ) socketStatus;
bytesRemaining -= ( size_t ) socketStatus;
configASSERT( bytesReceived + bytesRemaining == bytesRequested );
}
}
if( bytesReceived < bytesRequested )
{
IotLogWarn( "(Network connection %p) Receive requested %lu bytes, but %lu bytes received instead.",
pConnection,
( unsigned long ) bytesRequested,
( unsigned long ) bytesReceived );
}
else
{
IotLogDebug( "(Network connection %p) Successfully received %lu bytes.",
pConnection,
( unsigned long ) bytesRequested );
}
return bytesReceived;
}
/*-----------------------------------------------------------*/
size_t IotNetworkFreeRTOS_ReceiveUpto( IotNetworkConnection_t pConnection,
uint8_t * pBuffer,
size_t bufferSize )
{
int32_t socketStatus = 0;
size_t bytesReceived = 0;
/* Caller should never pass a zero-length buffer. */
configASSERT( bufferSize > 0 );
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
if( pConnection->secured == pdTRUE )
{
if( xSemaphoreTake( pConnection->socketMutex,
IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )
{
do
{
socketStatus = ( BaseType_t ) mbedtls_ssl_read( &( pConnection->ssl.context ),
pBuffer + bytesReceived,
bufferSize - bytesReceived );
} while( ( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) ||
( socketStatus == ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) );
xSemaphoreGive( pConnection->socketMutex );
}
else
{
IotLogError( "Could not obtain the socket mutex." );
}
}
else
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
{
socketStatus = FreeRTOS_recv( pConnection->socket,
pBuffer + bytesReceived,
bufferSize - bytesReceived,
0 );
}
if( socketStatus <= 0 )
{
IotLogError( "Error %ld while receiving data.", ( long int ) socketStatus );
}
else
{
bytesReceived += ( size_t ) socketStatus;
}
IotLogDebug( "Received %lu bytes.",
( unsigned long ) bytesReceived );
return bytesReceived;
}
/*-----------------------------------------------------------*/
IotNetworkError_t IotNetworkFreeRTOS_Close( IotNetworkConnection_t pConnection )
{
BaseType_t socketStatus = 0, i = 0;
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/* Notify the peer that the TLS connection is being closed. */
if( pConnection->secured == pdTRUE )
{
if( xSemaphoreTake( pConnection->socketMutex,
IOT_NETWORK_SOCKET_TIMEOUT_MS ) == pdTRUE )
{
socketStatus = ( BaseType_t ) mbedtls_ssl_close_notify( &( pConnection->ssl.context ) );
/* Ignore the WANT_READ and WANT_WRITE return values. */
if( ( socketStatus != ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_READ ) &&
( socketStatus != ( BaseType_t ) MBEDTLS_ERR_SSL_WANT_WRITE ) )
{
if( socketStatus == 0 )
{
IotLogInfo( "(Network connection %p) TLS close-notify sent.",
pConnection );
}
else
{
IotLogWarn( "(Network connection %p) Failed to send TLS close-notify, error %d.",
pConnection,
socketStatus );
}
}
xSemaphoreGive( pConnection->socketMutex );
}
}
#endif /* if ( IOT_NETWORK_ENABLE_TLS == 1 ) */
/* Call socket shutdown function to close connection. */
socketStatus = FreeRTOS_shutdown( pConnection->socket,
FREERTOS_SHUT_RDWR );
if( socketStatus != 0 )
{
IotLogWarn( "(Network connection %p) Failed to close connection.",
pConnection );
}
else
{
IotLogInfo( "(Network connection %p) Connection closed.",
pConnection );
}
/* Remove this connection from Select's socket set (if present). */
for( i = 0; i < IOT_NETWORK_MAX_RECEIVE_CALLBACKS; i++ )
{
if( Atomic_CompareAndSwapPointers_p32( &_connections[ i ], NULL, pConnection ) == 1 )
{
FreeRTOS_FD_CLR( pConnection->socket, _socketSet, eSELECT_ALL );
}
}
return IOT_NETWORK_SUCCESS;
}
/*-----------------------------------------------------------*/
IotNetworkError_t IotNetworkFreeRTOS_Destroy( IotNetworkConnection_t pConnection )
{
FreeRTOS_closesocket( pConnection->socket );
#if ( IOT_NETWORK_ENABLE_TLS == 1 )
/* Free mbed TLS contexts. */
if( pConnection->secured == pdTRUE )
{
_sslContextFree( pConnection );
}
#endif
/* Free memory used by network connection. */
vPortFree( pConnection );
IotLogInfo( "(Network connection %p) Connection destroyed.",
pConnection );
return IOT_NETWORK_SUCCESS;
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,365 @@
/*
* Amazon FreeRTOS Platform V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_threads_freertos.c
* @brief Implementation of the platform specific functions in iot_threads.h for
* FreeRTOS.
*/
/* The config header is always included first. */
#include "iot_config.h"
#include "semphr.h"
/* Platform threads include. */
#include "platform/iot_platform_types_freertos.h"
#include "platform/iot_threads.h"
#include "types/iot_platform_types.h"
/* Configure logs for the functions in this file. */
#ifdef IOT_LOG_LEVEL_PLATFORM
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_PLATFORM
#else
#ifdef IOT_LOG_LEVEL_GLOBAL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
#else
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
#endif
#endif
#define LIBRARY_LOG_NAME ( "THREAD" )
#include "iot_logging_setup.h"
/*
* Provide default values for undefined memory allocation functions based on
* the usage of dynamic memory allocation.
*/
#ifndef IotThreads_Malloc
#include <stdlib.h>
/**
* @brief Memory allocation. This function should have the same signature
* as [malloc](http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
#define IotThreads_Malloc malloc
#endif
#ifndef IotThreads_Free
#include <stdlib.h>
/**
* @brief Free memory. This function should have the same signature as
* [free](http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
#define IotThreads_Free free
#endif
/*-----------------------------------------------------------*/
static void _threadRoutineWrapper( void * pArgument )
{
threadInfo_t * pThreadInfo = ( threadInfo_t * ) pArgument;
/* Run the thread routine. */
pThreadInfo->threadRoutine( pThreadInfo->pArgument );
IotThreads_Free( pThreadInfo );
vTaskDelete( NULL );
}
/*-----------------------------------------------------------*/
bool Iot_CreateDetachedThread( IotThreadRoutine_t threadRoutine,
void * pArgument,
int32_t priority,
size_t stackSize )
{
bool status = true;
configASSERT( threadRoutine != NULL );
IotLogDebug( "Creating new thread." );
threadInfo_t * pThreadInfo = IotThreads_Malloc( sizeof( threadInfo_t ) );
if( pThreadInfo == NULL )
{
IotLogDebug( "Unable to allocate memory for threadRoutine %p.", threadRoutine );
status = false;
}
/* Create the FreeRTOS task that will run the thread. */
if( status )
{
pThreadInfo->threadRoutine = threadRoutine;
pThreadInfo->pArgument = pArgument;
if( xTaskCreate( _threadRoutineWrapper,
"iot_thread",
( configSTACK_DEPTH_TYPE ) stackSize,
pThreadInfo,
priority,
NULL ) != pdPASS )
{
/* Task creation failed. */
IotLogWarn( "Failed to create thread." );
IotThreads_Free( pThreadInfo );
status = false;
}
}
return status;
}
/*-----------------------------------------------------------*/
bool IotMutex_Create( IotMutex_t * pNewMutex,
bool recursive )
{
_IotSystemMutex_t * internalMutex = ( _IotSystemMutex_t * ) pNewMutex;
configASSERT( internalMutex != NULL );
IotLogDebug( "Creating new mutex %p.", pNewMutex );
if( recursive )
{
( void ) xSemaphoreCreateRecursiveMutexStatic( &internalMutex->xMutex );
}
else
{
( void ) xSemaphoreCreateMutexStatic( &internalMutex->xMutex );
}
/* remember the type of mutex */
if( recursive )
{
internalMutex->recursive = pdTRUE;
}
else
{
internalMutex->recursive = pdFALSE;
}
return true;
}
/*-----------------------------------------------------------*/
void IotMutex_Destroy( IotMutex_t * pMutex )
{
_IotSystemMutex_t * internalMutex = ( _IotSystemMutex_t * ) pMutex;
configASSERT( internalMutex != NULL );
vSemaphoreDelete( ( SemaphoreHandle_t ) &internalMutex->xMutex );
}
/*-----------------------------------------------------------*/
bool prIotMutexTimedLock( IotMutex_t * pMutex,
TickType_t timeout )
{
_IotSystemMutex_t * internalMutex = ( _IotSystemMutex_t * ) pMutex;
BaseType_t lockResult;
configASSERT( internalMutex != NULL );
IotLogDebug( "Locking mutex %p.", internalMutex );
/* Call the correct FreeRTOS mutex take function based on mutex type. */
if( internalMutex->recursive == pdTRUE )
{
lockResult = xSemaphoreTakeRecursive( ( SemaphoreHandle_t ) &internalMutex->xMutex, timeout );
}
else
{
lockResult = xSemaphoreTake( ( SemaphoreHandle_t ) &internalMutex->xMutex, timeout );
}
return( lockResult == pdTRUE );
}
/*-----------------------------------------------------------*/
void IotMutex_Lock( IotMutex_t * pMutex )
{
prIotMutexTimedLock( pMutex, portMAX_DELAY );
}
/*-----------------------------------------------------------*/
bool IotMutex_TryLock( IotMutex_t * pMutex )
{
return prIotMutexTimedLock( pMutex, 0 );
}
/*-----------------------------------------------------------*/
void IotMutex_Unlock( IotMutex_t * pMutex )
{
_IotSystemMutex_t * internalMutex = ( _IotSystemMutex_t * ) pMutex;
configASSERT( internalMutex != NULL );
IotLogDebug( "Unlocking mutex %p.", internalMutex );
/* Call the correct FreeRTOS mutex unlock function based on mutex type. */
if( internalMutex->recursive == pdTRUE )
{
( void ) xSemaphoreGiveRecursive( ( SemaphoreHandle_t ) &internalMutex->xMutex );
}
else
{
( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &internalMutex->xMutex );
}
}
/*-----------------------------------------------------------*/
bool IotSemaphore_Create( IotSemaphore_t * pNewSemaphore,
uint32_t initialValue,
uint32_t maxValue )
{
_IotSystemSemaphore_t * internalSemaphore = ( _IotSystemSemaphore_t * ) pNewSemaphore;
configASSERT( internalSemaphore != NULL );
IotLogDebug( "Creating new semaphore %p.", pNewSemaphore );
( void ) xSemaphoreCreateCountingStatic( maxValue, initialValue, &internalSemaphore->xSemaphore );
return true;
}
/*-----------------------------------------------------------*/
uint32_t IotSemaphore_GetCount( IotSemaphore_t * pSemaphore )
{
_IotSystemSemaphore_t * internalSemaphore = ( _IotSystemSemaphore_t * ) pSemaphore;
UBaseType_t count = 0;
configASSERT( internalSemaphore != NULL );
count = uxSemaphoreGetCount( ( SemaphoreHandle_t ) &internalSemaphore->xSemaphore );
IotLogDebug( "Semaphore %p has count %d.", pSemaphore, count );
return ( uint32_t ) count;
}
/*-----------------------------------------------------------*/
void IotSemaphore_Destroy( IotSemaphore_t * pSemaphore )
{
_IotSystemSemaphore_t * internalSemaphore = ( _IotSystemSemaphore_t * ) pSemaphore;
configASSERT( internalSemaphore != NULL );
IotLogDebug( "Destroying semaphore %p.", internalSemaphore );
vSemaphoreDelete( ( SemaphoreHandle_t ) &internalSemaphore->xSemaphore );
}
/*-----------------------------------------------------------*/
void IotSemaphore_Wait( IotSemaphore_t * pSemaphore )
{
_IotSystemSemaphore_t * internalSemaphore = ( _IotSystemSemaphore_t * ) pSemaphore;
configASSERT( internalSemaphore != NULL );
IotLogDebug( "Waiting on semaphore %p.", internalSemaphore );
/* Take the semaphore using the FreeRTOS API. */
if( xSemaphoreTake( ( SemaphoreHandle_t ) &internalSemaphore->xSemaphore,
portMAX_DELAY ) != pdTRUE )
{
IotLogWarn( "Failed to wait on semaphore %p.",
pSemaphore );
/* There is an assert here because during debugging we could falsely
* believe that we are waiting successfully on a semaphore. */
configASSERT( false );
}
}
/*-----------------------------------------------------------*/
bool IotSemaphore_TryWait( IotSemaphore_t * pSemaphore )
{
_IotSystemSemaphore_t * internalSemaphore = ( _IotSystemSemaphore_t * ) pSemaphore;
configASSERT( internalSemaphore != NULL );
IotLogDebug( "Attempting to wait on semaphore %p.", internalSemaphore );
return IotSemaphore_TimedWait( pSemaphore, 0 );
}
/*-----------------------------------------------------------*/
bool IotSemaphore_TimedWait( IotSemaphore_t * pSemaphore,
uint32_t timeoutMs )
{
_IotSystemSemaphore_t * internalSemaphore = ( _IotSystemSemaphore_t * ) pSemaphore;
configASSERT( internalSemaphore != NULL );
/* Take the semaphore using the FreeRTOS API. Cast the calculation to 64 bit to avoid overflows. */
if( xSemaphoreTake( ( SemaphoreHandle_t ) &internalSemaphore->xSemaphore,
pdMS_TO_TICKS( timeoutMs ) ) != pdTRUE )
{
/* Only warn if timeout > 0. */
if( timeoutMs > 0 )
{
IotLogWarn( "Timeout waiting on semaphore %p.",
internalSemaphore );
}
return false;
}
return true;
}
/*-----------------------------------------------------------*/
void IotSemaphore_Post( IotSemaphore_t * pSemaphore )
{
_IotSystemSemaphore_t * internalSemaphore = ( _IotSystemSemaphore_t * ) pSemaphore;
configASSERT( internalSemaphore != NULL );
IotLogDebug( "Posting to semaphore %p.", internalSemaphore );
/* Give the semaphore using the FreeRTOS API. */
BaseType_t result = xSemaphoreGive( ( SemaphoreHandle_t ) &internalSemaphore->xSemaphore );
if( result == pdFALSE )
{
IotLogDebug( "Unable to give semaphore over maximum", internalSemaphore );
}
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,110 @@
/*
* IoT Common V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_taskpool_internal.h
* @brief Internal header of task pool library. This header should not be included in
* typical application code.
*/
#ifndef IOT_TASKPOOL_INTERNAL_H_
#define IOT_TASKPOOL_INTERNAL_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Task pool include. */
#include "iot_error.h"
#include "iot_taskpool_freertos.h"
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "semphr.h"
#include "timers.h"
/* Configure logs for TASKPOOL functions. */
#ifdef IOT_LOG_LEVEL_TASKPOOL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_TASKPOOL
#else
#ifdef IOT_LOG_LEVEL_GLOBAL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
#else
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
#endif
#endif
#define LIBRARY_LOG_NAME ( "TASKPOOL" )
#include "iot_logging_setup.h"
/* ---------------------------------------------------------------------------------------------- */
/**
* @brief The task pool data structure keeps track of the internal state and the signals for the dispatcher threads.
* The task pool is a thread safe data structure.
*
* @warning This is a system-level data type that should not be modified or used directly in any application.
* @warning This is a system-level data type that can and will change across different versions of the platform, with no regards for backward compatibility.
*
*/
typedef struct _taskPool
{
IotDeQueue_t dispatchQueue; /**< @brief The queue for the jobs waiting to be executed. */
IotListDouble_t timerEventsList; /**< @brief The timeouts queue for all deferred jobs waiting to be executed. */
SemaphoreHandle_t dispatchSignal; /**< @brief The synchronization object on which threads are waiting for incoming jobs. */
StaticSemaphore_t dispatchSignalBuffer; /**< @brief The semaphore buffer. */
SemaphoreHandle_t xTimerEventMutex; /**< @brief The mutex for guarding the Timer Event Queue. */
StaticSemaphore_t xTimerEventMutexBuffer; /**< @brief The buffer for statically allocating the mutex. */
TimerHandle_t timer; /**< @brief The timer for deferred jobs. */
StaticTimer_t timerBuffer; /**< @brief The timer buffer. */
bool running; /**< @brief A flag to track whether the task pool is operational or should shut down. */
} _taskPool_t;
/**
* @brief Represents an operation that is subject to a timer.
*
* These events are queued per MQTT connection. They are sorted by their
* expiration time.
*/
typedef struct _taskPoolTimerEvent
{
IotLink_t link; /**< @brief List link member. */
TickType_t expirationTime; /**< @brief When this event should be processed. */
} _taskPoolTimerEvent_t;
/**
* @brief The job data structure keeps track of the user callback and context, as well as the status of the job.
*
* @warning This is a system-level data type that should not be modified or used directly in any application.
* @warning This is a system-level data type that can and will change across different versions of the platform, with no regards for backward compatibility.
*
*/
typedef struct _taskPoolJob
{
IotLink_t link; /**< @brief The link to insert the job in the dispatch queue. */
IotTaskPoolRoutine_t userCallback; /**< @brief The user provided callback. */
void * pUserContext; /**< @brief The user provided context. */
IotTaskPoolJobStatus_t status; /**< @brief The status for the job. */
_taskPoolTimerEvent_t timer; /**< @brief The timer for scheduling this job deferred. */
} _taskPoolJob_t;
#endif /* ifndef IOT_TASKPOOL_INTERNAL_H_ */

View file

@ -0,0 +1,305 @@
/*
* AWS IoT Common V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot.h
* @brief Provides routines and constants that are common to AWS IoT libraries.
* This header should not be included in typical application code.
*/
#ifndef AWS_IOT_H_
#define AWS_IOT_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <stdint.h>
/* Platform types include. */
#include "types/iot_platform_types.h"
/* MQTT types include. */
#include "types/iot_mqtt_types.h"
/**
* @brief The longest Thing Name accepted by AWS IoT, per the [AWS IoT
* Service Limits](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#limits_iot).
*/
#define AWS_IOT_MAX_THING_NAME_LENGTH ( 128 )
/**
* @brief The common prefix of all AWS IoT MQTT topics.
*/
#define AWS_IOT_TOPIC_PREFIX "$aws/things/"
/**
* @brief The length of #AWS_IOT_TOPIC_PREFIX.
*/
#define AWS_IOT_TOPIC_PREFIX_LENGTH ( ( uint16_t ) ( sizeof( AWS_IOT_TOPIC_PREFIX ) - 1 ) )
/**
* @brief The suffix for an AWS IoT operation "accepted" topic.
*/
#define AWS_IOT_ACCEPTED_SUFFIX "/accepted"
/**
* @brief The length of #AWS_IOT_ACCEPTED_SUFFIX.
*/
#define AWS_IOT_ACCEPTED_SUFFIX_LENGTH ( ( uint16_t ) ( sizeof( AWS_IOT_ACCEPTED_SUFFIX ) - 1 ) )
/**
* @brief The suffix for an AWS IoT operation "rejected" topic.
*/
#define AWS_IOT_REJECTED_SUFFIX "/rejected"
/**
* @brief The length of #AWS_IOT_REJECTED_SUFFIX.
*/
#define AWS_IOT_REJECTED_SUFFIX_LENGTH ( ( uint16_t ) ( sizeof( AWS_IOT_REJECTED_SUFFIX ) - 1 ) )
/**
* @brief The JSON key used to represent client tokens for AWS IoT.
*/
#define AWS_IOT_CLIENT_TOKEN_KEY "clientToken"
/**
* @brief The length of #AWS_IOT_CLIENT_TOKEN_KEY.
*/
#define AWS_IOT_CLIENT_TOKEN_KEY_LENGTH ( sizeof( AWS_IOT_CLIENT_TOKEN_KEY ) - 1 )
/**
* @brief The length of the longest client token allowed by AWS IoT.
*/
#define AWS_IOT_CLIENT_TOKEN_MAX_LENGTH ( 64 )
/**
* @brief A flag to represent persistent subscriptions in a subscriptions
* object.
*
* Its value is negative to distinguish it from valid subscription counts, which
* are 0 or positive.
*/
#define AWS_IOT_PERSISTENT_SUBSCRIPTION ( -1 )
/**
* @brief Function pointer representing an MQTT blocking operation.
*
* Currently, this is used to represent @ref mqtt_function_subscribesync or
* @ref mqtt_function_unsubscribesync.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription.
* @param[in] pSubscriptionList Pointer to the first element in the array of
* subscriptions.
* @param[in] subscriptionCount The number of elements in pSubscriptionList.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* Currently, flags are ignored by this function; this parameter is for
* future-compatibility.
* @param[in] timeoutMs If the MQTT server does not acknowledge the subscriptions within
* this timeout in milliseconds, this function returns #IOT_MQTT_TIMEOUT.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_TIMEOUT
* - #IOT_MQTT_SERVER_REFUSED
*/
typedef IotMqttError_t ( * AwsIotMqttFunction_t )( IotMqttConnection_t mqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint32_t flags,
uint32_t timeoutMs );
/**
* @brief Function pointer representing an MQTT library callback function.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage The received DELTA document (as an MQTT PUBLISH message).
*/
typedef void ( * AwsIotMqttCallbackFunction_t )( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Enumerations representing each of the statuses that may be parsed
* from a topic.
*/
typedef enum AwsIotStatus
{
AWS_IOT_ACCEPTED = 0, /**< Operation accepted. */
AWS_IOT_REJECTED = 1, /**< Operation rejected. */
AWS_IOT_UNKNOWN = 2 /**< Unknown status (neither accepted nor rejected). */
} AwsIotStatus_t;
/**
* @brief Information required to generate a topic for AWS IoT.
*/
typedef struct AwsIotTopicInfo
{
const char * pThingName; /**< @brief The Thing Name associated with the operation. */
size_t thingNameLength; /**< @brief The length of `pThingName`. */
const char * pOperationName; /**< @brief The operation name to place in the topic. */
uint16_t operationNameLength; /**< @brief The length of `pOperationName`. */
uint16_t longestSuffixLength; /**< @brief The length of longest suffix that will be placed at the end of the topic. */
void * ( *mallocString )( size_t size ); /**< @brief Function used to allocate a string, if needed. */
} AwsIotTopicInfo_t;
/**
* @brief Information needed to modify AWS IoT subscription topics.
*
* @warning The buffer passed as `pTopicFilterBase` must be large enough to
* accommodate the "/accepted" and "/rejected" suffixes.
*/
typedef struct AwsIotSubscriptionInfo_t
{
IotMqttConnection_t mqttConnection; /**< @brief The MQTT connection to use. */
AwsIotMqttCallbackFunction_t callbackFunction; /**< @brief Callback function for MQTT subscribe. */
uint32_t timeout; /**< @brief Timeout for MQTT function. */
/* Topic filter. */
char * pTopicFilterBase; /**< @brief Contains the base topic filter, without "/accepted" or "/rejected". */
uint16_t topicFilterBaseLength; /**< @brief Length of the base topic filter. */
} AwsIotSubscriptionInfo_t;
/**
* @brief Thing Name and length, used to match subscriptions.
*/
typedef struct AwsIotThingName
{
const char * pThingName; /**< @brief Thing Name to compare. */
size_t thingNameLength; /**< @brief Length of `pThingName`. */
} AwsIotThingName_t;
/**
* @brief Initializes the lists used by AWS IoT operations.
*
* @param[in] pPendingOperationsList The list that holds pending operations for
* a library.
* @param[in] pSubscriptionsList The list that holds subscriptions for a library.
* @param[in] pPendingOperationsMutex The mutex that guards the pending operations
* list.
* @param[in] pSubscriptionsMutex The mutex that guards the subscriptions list.
*
* @return `true` if all initialization succeeded; `false` otherwise.
*/
bool AwsIot_InitLists( IotListDouble_t * pPendingOperationsList,
IotListDouble_t * pSubscriptionsList,
IotMutex_t * pPendingOperationsMutex,
IotMutex_t * pSubscriptionsMutex );
/**
* @brief Checks that a Thing Name is valid for AWS IoT.
*
* @param[in] pThingName Thing Name to validate.
* @param[in] thingNameLength Length of `pThingName`.
*
* @return `true` if `pThingName` is valid; `false` otherwise.
*/
bool AwsIot_ValidateThingName( const char * pThingName,
size_t thingNameLength );
/**
* @brief Extracts the client token from a JSON document.
*
* The client token is used to differentiate AWS IoT operations. It is unique per
* operation.
*
* @param[in] pJsonDocument The JSON document to parse.
* @param[in] jsonDocumentLength The length of `pJsonDocument`.
* @param[out] pClientToken Set to point to the client token in `pJsonDocument`.
* @param[out] pClientTokenLength Set to the length of the client token.
*
* @return `true` if the client token was found; `false` otherwise. The output
* parameters are only valid if this function returns `true`.
*/
bool AwsIot_GetClientToken( const char * pJsonDocument,
size_t jsonDocumentLength,
const char ** pClientToken,
size_t * pClientTokenLength );
/**
* @brief Parse the Thing Name from an MQTT topic.
*
* @param[in] pTopicName The topic to parse.
* @param[in] topicNameLength The length of `pTopicName`.
* @param[out] pThingName Set to point to the Thing Name.
* @param[out] pThingNameLength Set to the length of the Thing Name.
*
* @return `true` if a Thing Name was successfully parsed; `false` otherwise. The output
* parameters are only valid if this function returns `true`.
*/
bool AwsIot_ParseThingName( const char * pTopicName,
uint16_t topicNameLength,
const char ** pThingName,
size_t * pThingNameLength );
/**
* @brief Parse the operation status (accepted or rejected) from an MQTT topic.
*
* @param[in] pTopicName The topic to parse.
* @param[in] topicNameLength The length of `pTopicName`.
*
* @return Any #AwsIotStatus_t.
*/
AwsIotStatus_t AwsIot_ParseStatus( const char * pTopicName,
uint16_t topicNameLength );
/**
* @brief Generate a topic to use for an AWS IoT operation.
*
* @param[in] pTopicInfo Information needed to generate an AWS IoT topic.
* @param[in,out] pTopicBuffer Where to place the generated topic. An existing
* buffer may be passed in. If `NULL`, this function will attempt to allocate a
* new buffer.
* @param[out] pOperationTopicLength Set to the length of the generated topic.
*
* @warning This function does not check the length of `pTopicBuffer`! Any provided
* buffer must be long enough to accommodate the Thing Name, operation name, and
* any other suffixes.
*
* @return `true` if the topic was successfully generated; `false` otherwise.
* This function will always succeed when an input buffer is provided.
*/
bool AwsIot_GenerateOperationTopic( const AwsIotTopicInfo_t * pTopicInfo,
char ** pTopicBuffer,
uint16_t * pOperationTopicLength );
/**
* @brief Add or remove subscriptions for AWS IoT operations.
*
* @param[in] mqttOperation Either @ref mqtt_function_subscribesync or
* @ref mqtt_function_unsubscribesync.
* @param[in] pSubscriptionInfo Information needed to process an MQTT
* operation.
*
* @return See the return values of @ref mqtt_function_subscribesync or
* @ref mqtt_function_unsubscribesync.
*/
IotMqttError_t AwsIot_ModifySubscriptions( AwsIotMqttFunction_t mqttOperation,
const AwsIotSubscriptionInfo_t * pSubscriptionInfo );
#endif /* ifndef AWS_IOT_H_ */

View file

@ -0,0 +1,69 @@
/*
* AWS IoT Common V1.0.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_doc_parser.h
* @brief Parser for AWS IoT Services Documents. This is a JSON parser
* specifically designed to process and retrieve a value from a AWS IoT JSON
* document, used in AWS IoT libraries such as Shadow and Jobs. Given a key and
* a JSON document, AwsIotDocParser_FindValue() will find the first occurrence
* of the key and return its respective value. The design goal of this parser
* is to be light weight and to be of low memory footprint. However, it does
* not check the correctness of the JSON documents. Hence, this parser is not
* meant to be used for general purpose JSON parsing.
*/
#ifndef AWS_IOT_DOC_PARSER_H_
#define AWS_IOT_DOC_PARSER_H_
/* Standard includes. */
#include <stdbool.h>
#include <stddef.h>
/**
* @brief Find a value for a key from a AWS IoT service JSON document.
*
* @warning The parsing will not check for the correctness of the JSON document.
* It is designed to be light weight and to be of low memory footprint rather
* than checking for the correctness of the JSON document. Hence this is not
* meant to be used for a general purpose JSON parsing. This is recommended to
* be used only with mutually authenticated AWS IoT services such as Shadow and
* Jobs where the document will always be a well formatted JSON.
*
* @param[in] pAwsIotJsonDocument Pointer to AWS IoT Service JSON document.
* @param[in] awsIotJsonDocumentLength Length of AWS IoT Service JSON document.
* @param[in] pAwsIotJsonKey JSON key for finding the associated value.
* @param[in] awsIotJsonKeyLength Length of the JSON key.
* @param[out] pAwsIotJsonValue Pointer to the pointer of value found.
* @param[out] pAwsIotJsonValueLength Pointer to the length of the value found.
*
* @returns `true` if a value is found, `false` if a value cannot be found. If
* returns `false`, the values in out pointers will not be valid.
*/
bool AwsIotDocParser_FindValue( const char * pAwsIotJsonDocument,
size_t awsIotJsonDocumentLength,
const char * pAwsIotJsonKey,
size_t awsIotJsonKeyLength,
const char ** pAwsIotJsonValue,
size_t * pAwsIotJsonValueLength );
#endif /* ifndef AWS_IOT_DOC_PARSER_H_ */

View file

@ -0,0 +1,294 @@
/*
* AWS IoT Common V1.0.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_doc_parser.c
* @brief Implements the functions in aws_iot_doc_parser.h
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* JSON utilities include. */
#include "aws_iot_doc_parser.h"
#define IS_QUOTE( str, idx ) \
( ( str )[ ( idx ) ] == '"' && ( ( idx ) == 0 || ( str )[ ( idx ) - 1 ] != '\\' ) )
#define IS_WHITESPACE( str, idx ) \
( ( str )[ ( idx ) ] == ' ' || ( str )[ ( idx ) ] == '\n' || ( str )[ ( idx ) ] == '\r' || ( str )[ ( idx ) ] == '\t' )
/*-----------------------------------------------------------*/
bool AwsIotDocParser_FindValue( const char * pAwsIotJsonDocument,
size_t awsIotJsonDocumentLength,
const char * pAwsIotJsonKey,
size_t awsIotJsonKeyLength,
const char ** pAwsIotJsonValue,
size_t * pAwsIotJsonValueLength )
{
size_t i = 0;
size_t jsonValueLength = 0;
char openCharacter = '\0', closeCharacter = '\0';
int nestingLevel = 0;
bool isWithinQuotes = false;
/* Validate all the arguments.*/
if( ( pAwsIotJsonDocument == NULL ) || ( pAwsIotJsonKey == NULL ) ||
( awsIotJsonDocumentLength == 0 ) || ( awsIotJsonKeyLength == 0 ) )
{
return false;
}
/* Ensure the JSON document is long enough to contain the key/value pair. At
* the very least, a JSON key/value pair must contain the key and the 6
* characters {":""} */
if( awsIotJsonDocumentLength < awsIotJsonKeyLength + 6 )
{
return false;
}
/* Search the characters in the JSON document for the key. The end of the JSON
* document does not have to be searched once too few characters remain to hold a
* value. */
while( i < awsIotJsonDocumentLength - awsIotJsonKeyLength - 3 )
{
/* If the first character in the key is found and there's an unescaped double
* quote after the key length, do a string compare for the key. */
if( ( IS_QUOTE( pAwsIotJsonDocument, i ) ) &&
( IS_QUOTE( pAwsIotJsonDocument, i + 1 + awsIotJsonKeyLength ) ) &&
( pAwsIotJsonDocument[ i + 1 ] == pAwsIotJsonKey[ 0 ] ) &&
( strncmp( pAwsIotJsonDocument + i + 1,
pAwsIotJsonKey,
awsIotJsonKeyLength ) == 0 ) )
{
/* Key found; this is a potential match. */
/* Skip the characters in the JSON key and closing double quote. */
/* While loop guarantees that i < awsIotJsonDocumentLength - 1 */
i += awsIotJsonKeyLength + 2;
/* Skip all whitespace characters between the closing " and the : */
while( IS_WHITESPACE( pAwsIotJsonDocument, i ) )
{
i++;
/* If the end of the document is reached, this isn't a match. */
if( i >= awsIotJsonDocumentLength )
{
return false;
}
}
/* The character immediately following a key (and any whitespace) must be a :
* If it's another character, then this string is a JSON value; skip it. */
if( pAwsIotJsonDocument[ i ] != ':' )
{
continue;
}
else
{
/* Skip the : */
i++;
}
/* If the end of the document is reached, this isn't a match. */
if( i >= awsIotJsonDocumentLength )
{
return false;
}
/* Skip all whitespace characters between : and the first character in the value. */
while( IS_WHITESPACE( pAwsIotJsonDocument, i ) )
{
i++;
/* If the end of the document is reached, this isn't a match. */
if( i >= awsIotJsonDocumentLength )
{
return false;
}
}
/* Value found. Set the output parameter. */
if( pAwsIotJsonValue != NULL )
{
*pAwsIotJsonValue = pAwsIotJsonDocument + i;
}
/* Calculate the value's length. */
switch( pAwsIotJsonDocument[ i ] )
{
/* Calculate length of a JSON string. */
case '\"':
/* Include the length of the opening and closing double quotes. */
jsonValueLength = 2;
/* Skip the opening double quote. */
i++;
/* If the end of the document is reached, this isn't a match. */
if( i >= awsIotJsonDocumentLength )
{
return false;
}
/* Add the length of all characters in the JSON string. */
while( pAwsIotJsonDocument[ i ] != '\"' )
{
/* Ignore escaped double quotes. */
if( ( pAwsIotJsonDocument[ i ] == '\\' ) &&
( i + 1 < awsIotJsonDocumentLength ) &&
( pAwsIotJsonDocument[ i + 1 ] == '\"' ) )
{
/* Skip the characters \" */
i += 2;
jsonValueLength += 2;
}
else
{
/* Add the length of a single character. */
i++;
jsonValueLength++;
}
/* If the end of the document is reached, this isn't a match. */
if( i >= awsIotJsonDocumentLength )
{
return false;
}
}
break;
/* Set the matching opening and closing characters of a JSON object or array.
* The length calculation is performed below. */
case '{':
openCharacter = '{';
closeCharacter = '}';
break;
case '[':
openCharacter = '[';
closeCharacter = ']';
break;
/* Calculate the length of a JSON primitive. */
default:
/* Skip the characters in the JSON value. The JSON value ends with a , or } */
while( pAwsIotJsonDocument[ i ] != ',' &&
pAwsIotJsonDocument[ i ] != '}' )
{
/* Any whitespace before a , or } means the JSON document is invalid. */
if( IS_WHITESPACE( pAwsIotJsonDocument, i ) )
{
return false;
}
i++;
jsonValueLength++;
/* If the end of the document is reached, this isn't a match. */
if( i >= awsIotJsonDocumentLength )
{
return false;
}
}
break;
}
/* Calculate the length of a JSON object or array. */
if( ( openCharacter != '\0' ) && ( closeCharacter != '\0' ) )
{
/* Include the length of the opening and closing characters. */
jsonValueLength = 2;
/* Skip the opening character. */
i++;
/* If the end of the document is reached, this isn't a match. */
if( i >= awsIotJsonDocumentLength )
{
return false;
}
/* Add the length of all characters in the JSON object or array. This
* includes the length of nested objects. */
while( pAwsIotJsonDocument[ i ] != closeCharacter ||
( pAwsIotJsonDocument[ i ] == closeCharacter && ( nestingLevel != 0 || isWithinQuotes ) ) )
{
/* Check if its a quote so as to avoid considering the
* nested levels for opening and closing characters within
* quotes.
*/
if( IS_QUOTE( pAwsIotJsonDocument, i ) )
{
/*Toggle the flag*/
isWithinQuotes = !isWithinQuotes;
}
/* Calculate the nesting levels only if not in quotes. */
if( !isWithinQuotes )
{
/* An opening character starts a nested object. */
if( pAwsIotJsonDocument[ i ] == openCharacter )
{
nestingLevel++;
}
/* A closing character ends a nested object. */
else if( pAwsIotJsonDocument[ i ] == closeCharacter )
{
nestingLevel--;
}
}
i++;
jsonValueLength++;
/* If the end of the document is reached, this isn't a match. */
if( i >= awsIotJsonDocumentLength )
{
return false;
}
}
}
/* JSON value length calculated; set the output parameter. */
if( pAwsIotJsonValueLength != NULL )
{
*pAwsIotJsonValueLength = jsonValueLength;
}
return true;
}
i++;
}
return false;
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,153 @@
/*
* AWS IoT Common V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_operation.c
* @brief Functions for common AWS IoT operations.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* Platform threads include. */
#include "platform/iot_threads.h"
/* AWS IoT include. */
#include "aws_iot.h"
/* Error handling include. */
#include "iot_error.h"
/*-----------------------------------------------------------*/
bool AwsIot_InitLists( IotListDouble_t * pPendingOperationsList,
IotListDouble_t * pSubscriptionsList,
IotMutex_t * pPendingOperationsMutex,
IotMutex_t * pSubscriptionsMutex )
{
IOT_FUNCTION_ENTRY( bool, true );
/* Flags to track cleanup. */
bool operationsMutexCreated = false;
bool subscriptionsMutexCreated = false;
/* Create the mutex guarding the pending operations list. */
operationsMutexCreated = IotMutex_Create( pPendingOperationsMutex, false );
if( operationsMutexCreated == false )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
/* Create the mutex guarding the subscriptions list. */
subscriptionsMutexCreated = IotMutex_Create( pSubscriptionsMutex, false );
if( subscriptionsMutexCreated == false )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
/* Initialize lists. */
IotListDouble_Create( pPendingOperationsList );
IotListDouble_Create( pSubscriptionsList );
IOT_FUNCTION_CLEANUP_BEGIN();
/* Clean up on error. */
if( status == false )
{
if( operationsMutexCreated == true )
{
IotMutex_Destroy( pPendingOperationsMutex );
}
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/
bool AwsIot_GenerateOperationTopic( const AwsIotTopicInfo_t * pTopicInfo,
char ** pTopicBuffer,
uint16_t * pOperationTopicLength )
{
bool status = true;
uint16_t bufferLength = 0;
uint16_t operationTopicLength = 0;
char * pBuffer = NULL;
/* Calculate the required topic buffer length. */
bufferLength = ( uint16_t ) ( AWS_IOT_TOPIC_PREFIX_LENGTH +
pTopicInfo->thingNameLength +
pTopicInfo->operationNameLength +
pTopicInfo->longestSuffixLength );
/* Allocate memory for the topic buffer if no topic buffer is given. */
if( *pTopicBuffer == NULL )
{
pBuffer = pTopicInfo->mallocString( ( size_t ) bufferLength );
if( pBuffer == NULL )
{
status = false;
}
}
/* Otherwise, use the given topic buffer. */
else
{
pBuffer = *pTopicBuffer;
}
if( status == true )
{
/* Copy the AWS IoT topic prefix into the topic buffer. */
( void ) memcpy( pBuffer, AWS_IOT_TOPIC_PREFIX, AWS_IOT_TOPIC_PREFIX_LENGTH );
operationTopicLength = ( uint16_t ) ( operationTopicLength + AWS_IOT_TOPIC_PREFIX_LENGTH );
/* Copy the Thing Name into the topic buffer. */
( void ) memcpy( pBuffer + operationTopicLength,
pTopicInfo->pThingName,
pTopicInfo->thingNameLength );
operationTopicLength = ( uint16_t ) ( operationTopicLength + pTopicInfo->thingNameLength );
/* Copy the Shadow operation string into the topic buffer. */
( void ) memcpy( pBuffer + operationTopicLength,
pTopicInfo->pOperationName,
pTopicInfo->operationNameLength );
operationTopicLength = ( uint16_t ) ( operationTopicLength + pTopicInfo->operationNameLength );
/* Set the output parameters. */
if( *pTopicBuffer == NULL )
{
*pTopicBuffer = pBuffer;
}
*pOperationTopicLength = operationTopicLength;
}
return status;
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,181 @@
/*
* AWS IoT Common V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_parser.c
* @brief Parses topics for Thing Name and status.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* AWS IoT include. */
#include "aws_iot.h"
/* Error handling include. */
#include "iot_error.h"
/* AWS Parser include. */
#include "aws_iot_doc_parser.h"
/**
* @brief Minimum allowed topic length for an AWS IoT status topic.
*
* Topics must contain at least:
* - The common prefix
* - The suffix "/accepted" or "/rejected"
* - 1 character for the Thing Name
* - 2 characters for the operation name and the enclosing slashes
*/
#define MINIMUM_TOPIC_NAME_LENGTH \
( uint16_t ) ( AWS_IOT_TOPIC_PREFIX_LENGTH + \
AWS_IOT_ACCEPTED_SUFFIX_LENGTH + \
1 + 2 )
/**
* @brief The longest client token accepted by AWS IoT service, per AWS IoT
* service limits.
*/
#define MAX_CLIENT_TOKEN_LENGTH ( 64 )
/*-----------------------------------------------------------*/
bool AwsIot_GetClientToken( const char * pJsonDocument,
size_t jsonDocumentLength,
const char ** pClientToken,
size_t * pClientTokenLength )
{
/* Extract the client token from the JSON document. */
bool status = AwsIotDocParser_FindValue( pJsonDocument,
jsonDocumentLength,
AWS_IOT_CLIENT_TOKEN_KEY,
AWS_IOT_CLIENT_TOKEN_KEY_LENGTH,
pClientToken,
pClientTokenLength );
if( status == true )
{
/* Check that the length of the client token is valid. */
if( ( *pClientTokenLength < 2 ) ||
( *pClientTokenLength > MAX_CLIENT_TOKEN_LENGTH ) )
{
status = false;
}
}
return status;
}
/*-----------------------------------------------------------*/
bool AwsIot_ParseThingName( const char * pTopicName,
uint16_t topicNameLength,
const char ** pThingName,
size_t * pThingNameLength )
{
IOT_FUNCTION_ENTRY( bool, true );
const char * pThingNameStart = NULL;
size_t thingNameLength = 0;
/* Check that the topic name is at least as long as the minimum allowed. */
if( topicNameLength < MINIMUM_TOPIC_NAME_LENGTH )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
/* Check that the given topic starts with the common prefix. */
if( strncmp( AWS_IOT_TOPIC_PREFIX,
pTopicName,
AWS_IOT_TOPIC_PREFIX_LENGTH ) != 0 )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
/* The Thing Name starts immediately after the topic prefix. */
pThingNameStart = pTopicName + AWS_IOT_TOPIC_PREFIX_LENGTH;
/* Calculate the length of the Thing Name, which is terminated with a '/'. */
while( ( thingNameLength + AWS_IOT_TOPIC_PREFIX_LENGTH < ( size_t ) topicNameLength ) &&
( pThingNameStart[ thingNameLength ] != '/' ) )
{
thingNameLength++;
}
/* The end of the topic name was reached without finding a '/'. The topic
* name is invalid. */
if( thingNameLength + AWS_IOT_TOPIC_PREFIX_LENGTH >= ( size_t ) topicNameLength )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
/* Set the output parameters. */
*pThingName = pThingNameStart;
*pThingNameLength = thingNameLength;
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
AwsIotStatus_t AwsIot_ParseStatus( const char * pTopicName,
uint16_t topicNameLength )
{
IOT_FUNCTION_ENTRY( AwsIotStatus_t, AWS_IOT_UNKNOWN );
const char * pSuffixStart = NULL;
/* Both 'accepted' and 'rejected' topics are of the same length
* The below is a defensive check at run time to ensure that.
*/
Iot_DefaultAssert( AWS_IOT_ACCEPTED_SUFFIX_LENGTH == AWS_IOT_REJECTED_SUFFIX_LENGTH );
/* Check that the status topic name is at least as long as the
* "accepted" suffix. This length check will be good for rejected also
* as both are of 8 characters in length. */
if( topicNameLength > AWS_IOT_ACCEPTED_SUFFIX_LENGTH )
{
/* Calculate where the "accepted" suffix should start. */
pSuffixStart = pTopicName + topicNameLength - AWS_IOT_ACCEPTED_SUFFIX_LENGTH;
/* Check if the end of the status topic name is "/accepted". */
if( strncmp( pSuffixStart,
AWS_IOT_ACCEPTED_SUFFIX,
AWS_IOT_ACCEPTED_SUFFIX_LENGTH ) == 0 )
{
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ACCEPTED );
}
/* Check if the end of the status topic name is "/rejected". */
if( strncmp( pSuffixStart,
AWS_IOT_REJECTED_SUFFIX,
AWS_IOT_REJECTED_SUFFIX_LENGTH ) == 0 )
{
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_REJECTED );
}
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,164 @@
/*
* AWS IoT Common V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_subscription.c
* @brief Functions for common AWS IoT subscriptions.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* AWS IoT include. */
#include "aws_iot.h"
/* Error handling include. */
#include "iot_error.h"
/* MQTT include. */
#include "iot_mqtt.h"
/**
* @brief Modify subscriptions, either by unsubscribing or subscribing.
*
* @param[in] mqttOperation Either @ref mqtt_function_subscribesync or @ref
* mqtt_function_unsubscribesync.
* @param[in] pSubscriptionInfo Information needed to process an MQTT
* operation.
* @param[in] pTopicFilter The topic filter to modify.
* @param[in] topicFilterLength The length of `pTopicFilter`.
*
* @return See the return values of @ref mqtt_function_subscribesync or
* @ref mqtt_function_unsubscribesync.
*/
static IotMqttError_t _modifySubscriptions( AwsIotMqttFunction_t mqttOperation,
const AwsIotSubscriptionInfo_t * pSubscriptionInfo,
const char * pTopicFilter,
uint16_t topicFilterLength );
/*-----------------------------------------------------------*/
static IotMqttError_t _modifySubscriptions( AwsIotMqttFunction_t mqttOperation,
const AwsIotSubscriptionInfo_t * pSubscriptionInfo,
const char * pTopicFilter,
uint16_t topicFilterLength )
{
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
/* Per the AWS IoT documentation, topic subscriptions are QoS 1. */
subscription.qos = IOT_MQTT_QOS_1;
/* Set the members of the subscription parameter. */
subscription.pTopicFilter = pTopicFilter;
subscription.topicFilterLength = topicFilterLength;
subscription.callback.pCallbackContext = NULL;
subscription.callback.function = pSubscriptionInfo->callbackFunction;
/* Call the MQTT operation function.
* Subscription count is 1 in this case.
* None of the flags are set in this call. Hence 0 is passed for flags.
* Please refer to documentation for AwsIotMqttFunction_t for more details.
*/
status = mqttOperation( pSubscriptionInfo->mqttConnection,
&subscription,
1,
0,
pSubscriptionInfo->timeout );
return status;
}
/*-----------------------------------------------------------*/
IotMqttError_t AwsIot_ModifySubscriptions( AwsIotMqttFunction_t mqttOperation,
const AwsIotSubscriptionInfo_t * pSubscriptionInfo )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_STATUS_PENDING );
IotMqttError_t acceptedStatus = IOT_MQTT_STATUS_PENDING;
uint16_t topicFilterLength = 0;
/* Place the topic "accepted" suffix at the end of the topic buffer. */
( void ) memcpy( pSubscriptionInfo->pTopicFilterBase
+ pSubscriptionInfo->topicFilterBaseLength,
AWS_IOT_ACCEPTED_SUFFIX,
AWS_IOT_ACCEPTED_SUFFIX_LENGTH );
topicFilterLength = ( uint16_t ) ( pSubscriptionInfo->topicFilterBaseLength
+ AWS_IOT_ACCEPTED_SUFFIX_LENGTH );
/* Modify the subscription to the "accepted" topic. */
acceptedStatus = _modifySubscriptions( mqttOperation,
pSubscriptionInfo,
pSubscriptionInfo->pTopicFilterBase,
topicFilterLength );
if( acceptedStatus != IOT_MQTT_SUCCESS )
{
IOT_SET_AND_GOTO_CLEANUP( acceptedStatus );
}
/* Place the topic "rejected" suffix at the end of the topic buffer. */
( void ) memcpy( pSubscriptionInfo->pTopicFilterBase
+ pSubscriptionInfo->topicFilterBaseLength,
AWS_IOT_REJECTED_SUFFIX,
AWS_IOT_REJECTED_SUFFIX_LENGTH );
topicFilterLength = ( uint16_t ) ( pSubscriptionInfo->topicFilterBaseLength
+ AWS_IOT_REJECTED_SUFFIX_LENGTH );
/* Modify the subscription to the "rejected" topic. */
status = _modifySubscriptions( mqttOperation,
pSubscriptionInfo,
pSubscriptionInfo->pTopicFilterBase,
topicFilterLength );
IOT_FUNCTION_CLEANUP_BEGIN();
/* Clean up on error. */
if( status != IOT_MQTT_SUCCESS )
{
/* Remove the subscription to the "accepted" topic if the subscription
* to the "rejected" topic failed. */
if( ( mqttOperation == IotMqtt_SubscribeSync ) &&
( acceptedStatus == IOT_MQTT_SUCCESS ) )
{
/* Place the topic "accepted" suffix at the end of the topic buffer. */
( void ) memcpy( pSubscriptionInfo->pTopicFilterBase
+ pSubscriptionInfo->topicFilterBaseLength,
AWS_IOT_ACCEPTED_SUFFIX,
AWS_IOT_ACCEPTED_SUFFIX_LENGTH );
topicFilterLength = ( uint16_t ) ( pSubscriptionInfo->topicFilterBaseLength
+ AWS_IOT_ACCEPTED_SUFFIX_LENGTH );
( void ) _modifySubscriptions( IotMqtt_UnsubscribeSync,
pSubscriptionInfo,
pSubscriptionInfo->pTopicFilterBase,
topicFilterLength );
}
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,62 @@
/*
* AWS IoT Common V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_validate.c
* @brief Validates Thing Names and other parameters to AWS IoT.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* AWS IoT include. */
#include "aws_iot.h"
/* Error handling include. */
#include "iot_error.h"
/*-----------------------------------------------------------*/
bool AwsIot_ValidateThingName( const char * pThingName,
size_t thingNameLength )
{
IOT_FUNCTION_ENTRY( bool, true );
if( pThingName == NULL )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
if( thingNameLength == 0 )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
if( thingNameLength > AWS_IOT_MAX_THING_NAME_LENGTH )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,10 @@
+ common
Contains the implementation of utility functions used by other AWS IoT libraries.
+ shadow
Contains the implementation of the AWS IoT Shadow client library.
+ jobs
Contain the implementation of the AWS IoT Jobs client library.
Further libraries will be rolled out soon.

View file

@ -0,0 +1,5 @@
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,11
[InternetShortcut]
IDList=
URL=https://www.freertos.org/jobs

View file

@ -0,0 +1,927 @@
/*
* AWS IoT Jobs V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_jobs.h
* @brief User-facing functions of the Jobs library.
*/
#ifndef AWS_IOT_JOBS_H_
#define AWS_IOT_JOBS_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Jobs types include. */
#include "types/aws_iot_jobs_types.h"
/*------------------------- Jobs library functions --------------------------*/
/**
* @functionspage{jobs,Jobs library}
* - @functionname{jobs_function_init}
* - @functionname{jobs_function_cleanup}
* - @functionname{jobs_function_getpendingasync}
* - @functionname{jobs_function_getpendingsync}
* - @functionname{jobs_function_startnextasync}
* - @functionname{jobs_function_startnextsync}
* - @functionname{jobs_function_describeasync}
* - @functionname{jobs_function_describesync}
* - @functionname{jobs_function_updateasync}
* - @functionname{jobs_function_updatesync}
* - @functionname{jobs_function_wait}
* - @functionname{jobs_function_setnotifypendingcallback}
* - @functionname{jobs_function_setnotifynextcallback}
* - @functionname{jobs_function_removepersistentsubscriptions}
* - @functionname{jobs_function_strerror}
* - @functionname{jobs_function_statename}
*/
/**
* @functionpage{AwsIotJobs_Init,jobs,init}
* @functionpage{AwsIotJobs_Cleanup,jobs,cleanup}
* @functionpage{AwsIotJobs_GetPendingAsync,jobs,getpendingasync}
* @functionpage{AwsIotJobs_GetPendingSync,jobs,getpendingsync}
* @functionpage{AwsIotJobs_StartNextAsync,jobs,startnextasync}
* @functionpage{AwsIotJobs_StartNextSync,jobs,startnextsync}
* @functionpage{AwsIotJobs_DescribeAsync,jobs,describeasync}
* @functionpage{AwsIotJobs_DescribeSync,jobs,describesync}
* @functionpage{AwsIotJobs_UpdateAsync,jobs,updateasync}
* @functionpage{AwsIotJobs_UpdateSync,jobs,updatesync}
* @functionpage{AwsIotJobs_Wait,jobs,wait}
* @functionpage{AwsIotJobs_SetNotifyPendingCallback,jobs,setnotifypendingcallback}
* @functionpage{AwsIotJobs_SetNotifyNextCallback,jobs,setnotifynextcallback}
* @functionpage{AwsIotJobs_RemovePersistentSubscriptions,jobs,removepersistentsubscriptions}
* @functionpage{AwsIotJobs_strerror,jobs,strerror}
* @functionpage{AwsIotJobs_StateName,jobs,statename}
*/
/**
* @brief One-time initialization function for the Jobs library.
*
* This function performs internal setup of the Jobs library. <b>It must be
* called once (and only once) before calling any other Jobs function.</b>
* Calling this function more than once without first calling @ref
* jobs_function_cleanup may result in a crash.
*
* @param[in] mqttTimeoutMs The amount of time (in milliseconds) that the Jobs
* library will wait for MQTT operations. Optional; set this to `0` to use
* @ref AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_INIT_FAILED
*
* @warning No thread-safety guarantees are provided for this function.
*
* @see @ref jobs_function_cleanup
*/
/* @[declare_jobs_init] */
AwsIotJobsError_t AwsIotJobs_Init( uint32_t mqttTimeoutMs );
/* @[declare_jobs_init] */
/**
* @brief One-time deinitialization function for the Jobs library.
*
* This function frees resources taken in @ref jobs_function_init and deletes
* any [persistent subscriptions.](@ref AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS)
* It should be called to clean up the Jobs library. After this function returns,
* @ref jobs_function_init must be called again before calling any other Jobs
* function.
*
* @warning No thread-safety guarantees are provided for this function.
*
* @see @ref jobs_function_init
*/
/* @[declare_jobs_cleanup] */
void AwsIotJobs_Cleanup( void );
/* @[declare_jobs_cleanup] */
/**
* @brief Get the list of all pending jobs for a Thing and receive an asynchronous
* notification when the response arrives.
*
* This function implements the [GetPendingJobExecutions]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-getpendingjobexecutions)
* command of the Jobs API, which gets the list of all Jobs for a Thing that are
* not in a terminal state. The list of retrieved Jobs is returned as the `pResponse`
* member in #AwsIotJobsCallbackParam_t, or through the #AwsIotJobsResponse_t
* parameter of @ref jobs_function_wait.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pGetPendingOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Jobs operation
* completes.
*
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
* queuing the Jobs operation.
* @return If this function fails before queuing the Jobs operation, it will return one of:
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
* @return Should the Jobs operation fail, the status will be one of:
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*
* @see @ref jobs_function_getpendingsync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* // Signature of Jobs callback function.
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
*
* AwsIotJobsOperation_t getPendingOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // Set the request info.
* requestInfo.mqttConnection = _mqttConnection;
* requestInfo.pThingName = THING_NAME;
* requestInfo.thingNameLength = THING_NAME_LENGTH;
*
* // Set the callback function to invoke.
* callbackInfo.function = _jobsCallback;
*
* // Queue Jobs GET PENDING.
* AwsIotJobsError_t getPendingResult = AwsIotJobs_GetPendingAsync( &requestInfo,
* 0,
* &callbackInfo,
* &getPendingOperation );
*
* // GET PENDING should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
* // _jobsCallback will be invoked once the Jobs response is received.
* @endcode
*/
/* @[declare_jobs_getpendingasync] */
AwsIotJobsError_t AwsIotJobs_GetPendingAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
AwsIotJobsOperation_t * const pGetPendingOperation );
/* @[declare_jobs_getpendingasync] */
/**
* @brief Get the list of all pending jobs for a Thing with a timeout for receiving
* the response.
*
* This function queues a Jobs GET PENDING, then waits for the result. Internally,
* this function is a call to @ref jobs_function_getpendingasync followed by
* @ref jobs_function_wait. See @ref jobs_function_getpendingasync for more information
* on the Jobs GET PENDING command.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] timeoutMs If a response is not received within this timeout, this
* function returns #AWS_IOT_JOBS_TIMEOUT.
* @param[out] pJobsResponse The response received from the Jobs service.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - #AWS_IOT_JOBS_TIMEOUT
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*/
/* @[declare_jobs_getpendingsync] */
AwsIotJobsError_t AwsIotJobs_GetPendingSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
uint32_t flags,
uint32_t timeoutMs,
AwsIotJobsResponse_t * const pJobsResponse );
/* @[declare_jobs_getpendingsync] */
/**
* @brief Start the next pending job execution for a Thing and receive an asynchronous
* notification when the response arrives.
*
* This function implements the [StartNextPendingJobExecution]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-startnextpendingjobexecution)
* command of the Jobs API, which gets and starts the next pending job execution
* for a Thing.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] pUpdateInfo Jobs update parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pStartNextOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Jobs operation
* completes.
*
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
* queuing the Jobs operation.
* @return If this function fails before queuing the Jobs operation, it will return one of:
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
* @return Should the Jobs operation fail, the status will be one of:
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*
* @see @ref jobs_function_startnextsync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* // Signature of Jobs callback function.
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
*
* AwsIotJobsOperation_t startNextOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
* AwsIotJobsUpdateInfo_t updateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // Set the request info. The update info generally does not need to be
* // changed, as its defaults are suitable.
* requestInfo.mqttConnection = _mqttConnection;
* requestInfo.pThingName = THING_NAME;
* requestInfo.thingNameLength = THING_NAME_LENGTH;
*
* // Set the callback function to invoke.
* callbackInfo.function = _jobsCallback;
*
* // Queue Jobs START NEXT.
* AwsIotJobsError_t startNextResult = AwsIotJobs_StartNextAsync( &requestInfo,
* &updateInfo,
* 0,
* &callbackInfo,
* &startNextOperation );
*
* // START NEXT should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
* // _jobsCallback will be invoked once the Jobs response is received.
* @endcode
*/
/* @[declare_jobs_startnextasync] */
AwsIotJobsError_t AwsIotJobs_StartNextAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
AwsIotJobsOperation_t * const pStartNextOperation );
/* @[declare_jobs_startnextasync] */
/**
* @brief Start the next pending job execution for a Thing with a timeout for
* receiving the response.
*
* This function queues a Jobs START NEXT, then waits for the result. Internally,
* this function is a call to @ref jobs_function_startnextasync followed by
* @ref jobs_function_wait. See @ref jobs_function_startnextasync for more information
* on the Jobs START NEXT command.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] pUpdateInfo Jobs update parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] timeoutMs If a response is not received within this timeout, this
* function returns #AWS_IOT_JOBS_TIMEOUT.
* @param[out] pJobsResponse The response received from the Jobs service.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - #AWS_IOT_JOBS_TIMEOUT
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*/
/* @[declare_jobs_startnextsync] */
AwsIotJobsError_t AwsIotJobs_StartNextSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
uint32_t flags,
uint32_t timeoutMs,
AwsIotJobsResponse_t * const pJobsResponse );
/* @[declare_jobs_startnextsync] */
/**
* @brief Get detailed information about a job execution and receive an asynchronous
* notification when the response arrives.
*
* This function implements the [DescribeJobExecution]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-describejobexecution)
* command of the Jobs API, which gets detailed information about a job execution.
* The description is returned as the `pResponse` member in #AwsIotJobsCallbackParam_t,
* or through the #AwsIotJobsResponse_t parameter of @ref jobs_function_wait.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] executionNumber The execution number to describe. Optional; pass
* #AWS_IOT_JOBS_NO_EXECUTION_NUMBER to ignore.
* @param[in] includeJobDocument Whether the response should include the full
* Job document.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pDescribeOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Jobs operation
* completes.
*
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
* queuing the Jobs operation.
* @return If this function fails before queuing the Jobs operation, it will return one of:
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
* @return Should the Jobs operation fail, the status will be one of:
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*
* @see @ref jobs_function_describesync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* // Signature of Jobs callback function.
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
*
* AwsIotJobsOperation_t describeOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // Set the request info.
* requestInfo.mqttConnection = _mqttConnection;
* requestInfo.pThingName = THING_NAME;
* requestInfo.thingNameLength = THING_NAME_LENGTH;
*
* // Describe the next Job. Or, this may be set to a specific Job ID.
* requestInfo.pJobId = AWS_IOT_JOBS_NEXT_JOB;
* requestInfo.jobIdLength = AWS_IOT_JOBS_NEXT_JOB_LENGTH;
*
* // Set the callback function to invoke.
* callbackInfo.function = _jobsCallback;
*
* // Queue Jobs DESCRIBE.
* AwsIotJobsError_t describeResult = AwsIotJobs_DescribeAsync( &requestInfo,
* AWS_IOT_JOBS_NO_EXECUTION_NUMBER,
* false,
* 0,
* &callbackInfo,
* &describeOperation );
*
* // DESCRIBE should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
* // _jobsCallback will be invoked once the Jobs response is received.
* @endcode
*/
/* @[declare_jobs_describeasync] */
AwsIotJobsError_t AwsIotJobs_DescribeAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
int32_t executionNumber,
bool includeJobDocument,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
AwsIotJobsOperation_t * const pDescribeOperation );
/* @[declare_jobs_describeasync] */
/**
* @brief Get detailed information about a job execution with a timeout for receiving
* the response.
*
* This function queues a Jobs DESCRIBE, then waits for the result. Internally,
* this function is a call to @ref jobs_function_describeasync followed by
* @ref jobs_function_wait. See @ref jobs_function_describeasync for more information
* on the Jobs DESCRIBE command.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] executionNumber The execution number to describe. Optional; pass
* #AWS_IOT_JOBS_NO_EXECUTION_NUMBER to ignore.
* @param[in] includeJobDocument Whether the response should include the full
* Job document.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] timeoutMs If a response is not received within this timeout, this
* function returns #AWS_IOT_JOBS_TIMEOUT.
* @param[out] pJobsResponse The response received from the Jobs service.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - #AWS_IOT_JOBS_TIMEOUT
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*/
/* @[declare_jobs_describesync] */
AwsIotJobsError_t AwsIotJobs_DescribeSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
int32_t executionNumber,
bool includeJobDocument,
uint32_t flags,
uint32_t timeoutMs,
AwsIotJobsResponse_t * const pJobsResponse );
/* @[declare_jobs_describesync] */
/**
* @brief Update the status of a job execution and receive an asynchronous
* notification when the Job update completes.
*
* This function implements the [UpdateJobExecution]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-updatejobexecution)
* command of the Jobs API, which updates the status of a Job execution.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] pUpdateInfo Jobs update parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pUpdateOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Jobs operation
* completes.
*
* @return This function will return #AWS_IOT_JOBS_STATUS_PENDING upon successfully
* queuing the Jobs operation.
* @return If this function fails before queuing the Jobs operation, it will return one of:
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* @return Upon successful completion of the Jobs operation (either through an #AwsIotJobsCallbackInfo_t
* or @ref jobs_function_wait), the status will be #AWS_IOT_JOBS_SUCCESS.
* @return Should the Jobs operation fail, the status will be one of:
* - #AWS_IOT_JOBS_NO_MEMORY (Memory could not be allocated for incoming document)
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*
* @see @ref jobs_function_updatesync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* // Signature of Jobs callback function.
* void _jobsCallback( void * pCallbackContext, AwsIotJobsCallbackParam_t * pCallbackParam );
*
* AwsIotJobsOperation_t updateOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
* AwsIotJobsUpdateInfo_t updateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;
* AwsIotJobsCallbackInfo_t callbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // Set the request info.
* requestInfo.mqttConnection = _mqttConnection;
* requestInfo.pThingName = THING_NAME;
* requestInfo.thingNameLength = THING_NAME_LENGTH;
*
* // A Job ID must be set. AWS_IOT_JOBS_NEXT_JOB is not valid for UPDATE.
* requestInfo.pJobId = "job-id";
* requestInfo.jobIdLength = 6;
*
* // Set the update info.
* updateInfo.newStatus = AWS_IOT_JOB_STATE_SUCCEEDED;
*
* // Set the callback function to invoke.
* callbackInfo.function = _jobsCallback;
*
* // Queue Jobs UPDATE.
* AwsIotJobsError_t updateResult = AwsIotJobs_UpdateAsync( &requestInfo,
* &updateInfo,
* 0,
* &callbackInfo,
* &updateOperation );
*
* // UPDATE should have returned AWS_IOT_JOBS_STATUS_PENDING. The function
* // _jobsCallback will be invoked once the Jobs response is received.
* @endcode
*/
/* @[declare_jobs_updateasync] */
AwsIotJobsError_t AwsIotJobs_UpdateAsync( const AwsIotJobsRequestInfo_t * pRequestInfo,
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
AwsIotJobsOperation_t * const pUpdateOperation );
/* @[declare_jobs_updateasync] */
/**
* @brief Update the status of a job execution with a timeout for receiving the
* response.
*
* This function queues a Jobs UPDATE, then waits for the result. Internally,
* this function is a call to @ref jobs_function_updateasync followed by
* @ref jobs_function_wait. See @ref jobs_function_updateasync for more information
* on the Jobs UPDATE command.
*
* @param[in] pRequestInfo Jobs request parameters.
* @param[in] pUpdateInfo Jobs update parameters.
* @param[in] flags Flags which modify the behavior of the Jobs request. See
* @ref jobs_constants_flags.
* @param[in] timeoutMs If a response is not received within this timeout, this
* function returns #AWS_IOT_JOBS_TIMEOUT.
* @param[out] pJobsResponse The response received from the Jobs service.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - #AWS_IOT_JOBS_TIMEOUT
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*/
/* @[declare_jobs_updatesync] */
AwsIotJobsError_t AwsIotJobs_UpdateSync( const AwsIotJobsRequestInfo_t * pRequestInfo,
const AwsIotJobsUpdateInfo_t * pUpdateInfo,
uint32_t flags,
uint32_t timeoutMs,
AwsIotJobsResponse_t * const pJobsResponse );
/* @[declare_jobs_updatesync] */
/**
* @brief Wait for a Jobs operation to complete.
*
* This function blocks to wait for a [GET PENDING](@ref jobs_function_getpendingasync),
* [START NEXT](@ref jobs_function_startnextasync), [DESCRIBE](@ref jobs_function_describeasync),
* or [UPDATE](@ref jobs_function_updateasync) operation to complete. These operations are
* by default asynchronous; the function calls queue an operation for processing,
* and a callback is invoked once the operation is complete.
*
* To use this function, the flag #AWS_IOT_JOBS_FLAG_WAITABLE must have been
* set in the operation's function call. Additionally, this function must always
* be called with any waitable operation to clean up resources.
*
* Regardless of its return value, this function always clean up resources used
* by the waitable operation. This means `operation` is invalidated as soon as
* this function returns, even if it returns #AWS_IOT_JOBS_TIMEOUT or another
* error.
*
* @param[in] operation Reference to the Jobs operation to wait for. The flag
* #AWS_IOT_JOBS_FLAG_WAITABLE must have been set for this operation.
* @param[in] timeoutMs How long to wait before returning #AWS_IOT_JOBS_TIMEOUT.
* @param[out] pJobsResponse The response received from the Jobs service.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_BAD_RESPONSE
* - #AWS_IOT_JOBS_TIMEOUT
* - A Jobs failure reason between #AWS_IOT_JOBS_INVALID_TOPIC and #AWS_IOT_JOBS_TERMINAL_STATE.
*
* <b>Example</b>:
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* AwsIotJobsOperation_t updateOperation = AWS_IOT_JOBS_OPERATION_INITIALIZER;
* AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
* AwsIotJobsUpdateInfo_t updateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;
* AwsIotJobsResponse_t jobsResponse = AWS_IOT_JOBS_RESPONSE_INITIALIZER;
*
* // Set the request info.
* requestInfo.mqttConnection = _mqttConnection;
* requestInfo.pThingName = THING_NAME;
* requestInfo.thingNameLength = THING_NAME_LENGTH;
*
* // Set the function used to allocate memory for an incoming response.
* requestInfo.mallocResponse = malloc;
*
* // A Job ID must be set. AWS_IOT_JOBS_NEXT_JOB is not valid for UPDATE.
* requestInfo.pJobId = "job-id";
* requestInfo.jobIdLength = 6;
*
* // Set the update info.
* updateInfo.newStatus = AWS_IOT_JOB_STATE_SUCCEEDED;
*
* // Queue Jobs UPDATE.
* AwsIotJobsError_t updateResult = AwsIotJobs_UpdateAsync( &requestInfo,
* &updateInfo,
* AWS_IOT_JOBS_FLAG_WAITABLE,
* NULL,
* &updateOperation );
*
* // UPDATE should have returned AWS_IOT_JOBS_STATUS_PENDING. The call to wait
* // returns once the result of the UPDATE is available or the timeout expires.
* if( updateResult == AWS_IOT_JOBS_STATUS_PENDING )
* {
* updateResult = AwsIotJobs_Wait( updateOperation, 5000, &jobsResponse );
*
* if( updateResult == AWS_IOT_JOBS_SUCCESS )
* {
* // Jobs operation succeeded. Do something with the Jobs response.
*
* // Once the Jobs response is no longer needed, free it.
* free( jobsResponse.pJobsResponse );
* }
* else
* {
* // Jobs operation failed.
* }
* }
* @endcode
*/
/* @[declare_jobs_wait] */
AwsIotJobsError_t AwsIotJobs_Wait( AwsIotJobsOperation_t operation,
uint32_t timeoutMs,
AwsIotJobsResponse_t * const pJobsResponse );
/* @[declare_jobs_wait] */
/**
* @brief Set a callback to be invoked when the list of pending Jobs changes.
*
* The Jobs service publishes a [JobExecutionsChanged]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-jobexecutionschanged)
* message to the `jobs/notify` topic whenever a Job execution is added to or
* removed from the list of pending Job executions for a Thing. The message sent is
* useful for monitoring the list of pending Job executions.
*
* A <i>NOTIFY PENDING</i> callback may be invoked whenever a message is published
* to `jobs/notify`. Each Thing may have up to @ref AWS_IOT_JOBS_NOTIFY_CALLBACKS
* NOTIFY PENDING callbacks set. This function modifies the NOTIFY PENDING callback
* for a specific Thing depending on the `pNotifyPendingCallback` parameter and the
* presence of any existing NOTIFY PENDING callback.
* - When no existing NOTIFY PENDING callback exists for a specific Thing, a new
* callback is added.
* - If there is an existing NOTIFY PENDING callback and `pNotifyPendingCallback` is not `NULL`,
* then the existing callback function and parameter are replaced with `pNotifyPendingCallback`.
* - If there is an existing NOTIFY PENDING callback and `pNotifyPendingCallback` is `NULL`,
* then the callback is removed.
*
* The member @ref AwsIotJobsCallbackInfo_t.oldFunction must be used to select an
* already-registered callback function for replacement or removal when @ref
* AWS_IOT_JOBS_NOTIFY_CALLBACKS is greater than `1`. When multiple callbacks are
* set, all of them will be invoked when a message is received.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription to `jobs/notify`.
* @param[in] pThingName The subscription to `jobs/notify` will be added for
* this Thing Name.
* @param[in] thingNameLength The length of `pThingName`.
* @param[in] flags This parameter is for future-compatibility. Currently, flags are
* not supported for this function and this parameter is ignored.
* @param[in] pNotifyPendingCallback Callback function to invoke for incoming messages.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_TIMEOUT
*
* @see @ref jobs_function_setnotifynextcallback for the function to register callbacks
* for next Job changes.
*
* <b>Example</b>:
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* AwsIotJobsError_t result = AWS_IOT_JOBS_STATUS_PENDING;
* AwsIotJobsCallbackInfo_t notifyPendingCallback = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // _jobsCallback will be invoked when any messages are received.
* notifyPendingCallback.function = _jobsCallback;
*
* // Set the NOTIFY PENDING callback for the Thing "Test_device".
* result = AwsIotJobs_SetNotifyPendingCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* &notifyPendingCallback );
*
* // Check if the callback was successfully set.
* if( status == AWS_IOT_JOBS_SUCCESS )
* {
* // The callback will now be invoked whenever the list of pending Job
* // executions changes.
*
* // Once the callback is no longer needed, it may be removed by passing
* // NULL as the callback function and specifying the function to remove.
* notifyPendingCallback.function = NULL;
* notifyPendingCallback.oldFunction = _jobsCallback;
*
* status = AwsIotJobs_SetNotifyPendingCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* &notifyPendingCallback );
*
* // The return value from removing a callback should always be success.
* assert( status == AWS_IOT_JOBS_SUCCESS );
* }
* @endcode
*/
/* @[declare_jobs_setnotifypendingcallback] */
AwsIotJobsError_t AwsIotJobs_SetNotifyPendingCallback( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pNotifyPendingCallback );
/* @[declare_jobs_setnotifypendingcallback] */
/**
* @brief Set a callback to be invoked when the next pending Job changes.
*
* The Jobs service publishes a [NextJobExecutionChanged]
* (https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-nextjobexecutionchanged)
* message to the `jobs/notify-next` topic whenever the next Job execution in
* the list of pending Job executions changes for a Thing. The message sent is
* useful for being notified of changes to the next Job.
*
* A <i>NOTIFY NEXT</i> callback may be invoked whenever a message is published
* to `jobs/notify-next`. Each Thing may have up to @ref AWS_IOT_JOBS_NOTIFY_CALLBACKS
* NOTIFY NEXT callbacks set. This function modifies the NOTIFY NEXT callback for
* a specific Thing depending on the `pNotifyNextCallback` parameter and the presence
* of any existing NOTIFY NEXT callback.
* - When no existing NOTIFY NEXT callback exists for a specific Thing, a new
* callback is added.
* - If there is an existing NOTIFY NEXT callback and `pNotifyNextCallback` is not `NULL`,
* then the existing callback function and parameter are replaced with `pNotifyNextCallback`.
* - If there is an existing NOTIFY NEXT callback and `pNotifyNextCallback` is `NULL`,
* then the callback is removed.
*
* The member @ref AwsIotJobsCallbackInfo_t.oldFunction must be used to select an
* already-registered callback function for replacement or removal when @ref
* AWS_IOT_JOBS_NOTIFY_CALLBACKS is greater than `1`. When multiple callbacks are
* set, all of them will be invoked when a message is received.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription to `jobs/notify-next`.
* @param[in] pThingName The subscription to `jobs/notify-next` will be added for
* this Thing Name.
* @param[in] thingNameLength The length of `pThingName`.
* @param[in] flags This parameter is for future-compatibility. Currently, flags are
* not supported for this function and this parameter is ignored.
* @param[in] pNotifyNextCallback Callback function to invoke for incoming messages.
*
* @return One of the following:
* - #AWS_IOT_JOBS_SUCCESS
* - #AWS_IOT_JOBS_NOT_INITIALIZED
* - #AWS_IOT_JOBS_BAD_PARAMETER
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
* - #AWS_IOT_JOBS_TIMEOUT
*
* @see @ref jobs_function_setnotifypendingcallback for the function to register callbacks
* for all pending Job changes.
*
* <b>Example</b>:
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* AwsIotJobsError_t result = AWS_IOT_JOBS_STATUS_PENDING;
* AwsIotJobsCallbackInfo_t notifyNextCallback = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;
*
* // _jobsCallback will be invoked when any messages are received.
* notifyNextCallback.function = _jobsCallback;
*
* // Set the NOTIFY NEXT callback for the Thing "Test_device".
* result = AwsIotJobs_SetNotifyNextCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* &notifyNextCallback );
*
* // Check if the callback was successfully set.
* if( status == AWS_IOT_JOBS_SUCCESS )
* {
* // The callback will now be invoked whenever the next pending Job
* // execution changes.
*
* // Once the callback is no longer needed, it may be removed by passing
* // NULL as the callback function and specifying the function to remove.
* notifyNextCallback.function = NULL;
* notifyNextCallback.oldFunction = _jobsCallback;
*
* status = AwsIotJobs_SetNotifyNextCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* &notifyNextCallback );
*
* // The return value from removing a callback should always be success.
* assert( status == AWS_IOT_JOBS_SUCCESS );
* }
* @endcode
*/
/* @[declare_jobs_setnotifynextcallback] */
AwsIotJobsError_t AwsIotJobs_SetNotifyNextCallback( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pNotifyNextCallback );
/* @[declare_jobs_setnotifynextcallback] */
/**
* @brief Remove persistent Jobs operation topic subscriptions.
*
* Passing the flag @ref AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS to @ref jobs_function_getpendingasync,
* @ref jobs_function_startnextasync, @ref jobs_function_describeasync, @ref jobs_function_updateasync,
* or their blocking versions causes the Jobs operation topic subscriptions to be
* maintained for future calls to the same function. If a persistent subscription for a
* Jobs topic are no longer needed, this function may be used to remove it.
*
* @param[in] pRequestInfo Jobs request info. Only the [pThingName]
* (@ref #AwsIotJobsRequestInfo_t.pThingName), [thingNameLength]
* (@ref #AwsIotJobsRequestInfo_t.thingNameLength), and [mqttConnection]
* (@ref #AwsIotJobsRequestInfo_t.mqttConnection) members need to be set for this
* function.
* @param[in] flags Flags that determine which subscriptions to remove. Valid values are
* the bitwise OR of the following individual flags:
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_GET_PENDING_SUBSCRIPTIONS
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_START_NEXT_SUBSCRIPTIONS
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_DESCRIBE_SUBSCRIPTIONS
* - @ref AWS_IOT_JOBS_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS
*
* @return On success:
* - #AWS_IOT_JOBS_SUCCESS
* @return If an MQTT UNSUBSCRIBE packet cannot be sent, one of the following:
* - #AWS_IOT_JOBS_NO_MEMORY
* - #AWS_IOT_JOBS_MQTT_ERROR
*
* @note @ref jobs_function_cleanup removes persistent sessions as well.
*
* @warning This function is not safe to call with any in-progress operations!
* It also does not affect NOTIFY PENDING and NOTIFY NEXT callbacks registered
* with @ref jobs_function_setnotifypendingcallback and
* @ref jobs_function_setnotifynextcallback, respectively. (See documentation for
* those functions on how to remove their callbacks).
*/
/* @[declare_jobs_removepersistentsubscriptions] */
AwsIotJobsError_t AwsIotJobs_RemovePersistentSubscriptions( const AwsIotJobsRequestInfo_t * pRequestInfo,
uint32_t flags );
/* @[declare_jobs_removepersistentsubscriptions] */
/*-------------------------- Jobs helper functions --------------------------*/
/**
* @brief Returns a string that describes an #AwsIotJobsError_t.
*
* Like POSIX's `strerror`, this function returns a string describing a return
* code. In this case, the return code is a Jobs library error code, `status`.
*
* The string returned by this function <b>MUST</b> be treated as read-only: any
* attempt to modify its contents may result in a crash. Therefore, this function
* is limited to usage in logging.
*
* @param[in] status The status to describe.
*
* @return A read-only string that describes `status`.
*
* @warning The string returned by this function must never be modified.
*/
/* @[declare_jobs_strerror] */
const char * AwsIotJobs_strerror( AwsIotJobsError_t status );
/* @[declare_jobs_strerror] */
/**
* @brief Returns a string that describes an #AwsIotJobState_t.
*
* This function returns a string describing a Job state, `state`.
*
* The string returned by this function <b>MUST</b> be treated as read-only: any
* attempt to modify its contents may result in a crash. Therefore, this function
* is limited to usage in logging.
*
* @param[in] state The job state to describe.
*
* @return A read-only string that describes `state`.
*
* @warning The string returned by this function must never be modified.
*/
/* @[declare_jobs_statename] */
const char * AwsIotJobs_StateName( AwsIotJobState_t state );
/* @[declare_jobs_statename] */
#endif /* ifndef AWS_IOT_JOBS_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,885 @@
/*
* AWS IoT Jobs V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_jobs_operation.c
* @brief Implements functions that process Jobs operations.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* Jobs internal include. */
#include "private/aws_iot_jobs_internal.h"
/* Platform threads include. */
#include "platform/iot_threads.h"
/* Error handling include. */
#include "iot_error.h"
/* MQTT include. */
#include "iot_mqtt.h"
/*-----------------------------------------------------------*/
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
/**
* @brief Printable names for each of the Jobs operations.
*/
const char * const _pAwsIotJobsOperationNames[] =
{
"GET PENDING",
"START NEXT",
"DESCRIBE",
"UPDATE",
"SET NOTIFY-PENDING",
"SET NOTIFY-NEXT"
};
#endif /* if LIBRARY_LOG_LEVEL > IOT_LOG_NONE */
/*-----------------------------------------------------------*/
/**
* @brief First parameter to #_jobsOperation_match.
*/
typedef struct _operationMatchParams
{
_jobsOperationType_t type; /**< @brief GET PENDING, START NEXT, DESCRIBE, or UPDATE. */
const char * pThingName; /**< @brief Thing Name of Jobs operation. */
size_t thingNameLength; /**< @brief Length of #_operationMatchParams_t.pThingName. */
const char * pResponse; /**< @brief Jobs response document. */
size_t responseLength; /**< @brief Length of #_operationMatchParams_t.pResponse. */
} _operationMatchParams_t;
/*-----------------------------------------------------------*/
/**
* @brief Match a received Jobs response with a Jobs operation awaiting a
* response.
*
* @param[in] pOperationLink Pointer to the link member of the #_jobsOperation_t
* to check.
* @param[in] pMatch Pointer to an #_operationMatchParams_t.
*
* @return `true` if `pMatch` matches the received response; `false` otherwise.
*/
static bool _jobsOperation_match( const IotLink_t * pOperationLink,
void * pMatch );
/**
* @brief Invoked when a Jobs response is received for Jobs GET PENDING.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
*/
static void _getPendingCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Invoked when a Jobs response is received for a Jobs START NEXT.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
*/
static void _startNextCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Invoked when a Jobs response is received for Jobs DESCRIBE.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
*/
static void _describeCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Invoked when a Jobs response is received for a Jobs UPDATE.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
*/
static void _updateCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Common function for processing received Jobs responses.
*
* @param[in] type GET PENDING, START NEXT, DESCRIBE, or UPDATE.
* @param[in] pMessage Received Jobs response (as an MQTT PUBLISH message).
*/
static void _commonOperationCallback( _jobsOperationType_t type,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Notify of a completed Jobs operation.
*
* @param[in] pOperation The operation which completed.
*
* Depending on the parameters passed to a user-facing Jobs function, the
* notification will cause @ref jobs_function_wait to return or invoke a
* user-provided callback.
*/
static void _notifyCompletion( _jobsOperation_t * pOperation );
/**
* @brief Get a Jobs subscription to use with a Jobs operation.
*
* This function may use an existing Jobs subscription, or it may allocate a
* new one.
*
* @param[in] pRequestInfo Common Jobs request parameters.
* @param[in] pTopicBuffer Contains the topic to use for subscribing.
* @param[in] operationTopicLength The length of the base topic in `pTopicBuffer`.
* @param[in] pOperation Jobs operation that needs a subscription.
* @param[out] pFreeTopicBuffer Whether the caller may free `pTopicBuffer`
* (which may be assigned to a subscription).
*
* @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY
*/
static AwsIotJobsError_t _findSubscription( const AwsIotJobsRequestInfo_t * pRequestInfo,
char * pTopicBuffer,
uint16_t operationTopicLength,
_jobsOperation_t * pOperation,
bool * pFreeTopicBuffer );
/*-----------------------------------------------------------*/
/**
* @brief List of active Jobs operations awaiting a response from the Jobs
* service.
*/
IotListDouble_t _AwsIotJobsPendingOperations = { 0 };
/**
* @brief Protects #_AwsIotJobsPendingOperations from concurrent access.
*/
IotMutex_t _AwsIotJobsPendingOperationsMutex;
/*-----------------------------------------------------------*/
static bool _jobsOperation_match( const IotLink_t * pOperationLink,
void * pMatch )
{
/* Because this function is called from a container function, the given link
* must never be NULL. */
AwsIotJobs_Assert( pOperationLink != NULL );
_jobsOperation_t * pOperation = IotLink_Container( _jobsOperation_t,
pOperationLink,
link );
_operationMatchParams_t * pParam = ( _operationMatchParams_t * ) pMatch;
_jobsSubscription_t * pSubscription = pOperation->pSubscription;
const char * pClientToken = NULL;
size_t clientTokenLength = 0;
/* Check for matching Thing Name and operation type. */
bool match = ( pOperation->type == pParam->type ) &&
( pParam->thingNameLength == pSubscription->thingNameLength ) &&
( strncmp( pParam->pThingName,
pSubscription->pThingName,
pParam->thingNameLength ) == 0 );
if( match == true )
{
IotLogDebug( "Verifying client tokens for Jobs %s.",
_pAwsIotJobsOperationNames[ pParam->type ] );
/* Check the response for a client token. */
match = AwsIot_GetClientToken( pParam->pResponse,
pParam->responseLength,
&pClientToken,
&clientTokenLength );
/* If the response contains a client token, check for a match. */
if( match == true )
{
match = ( pOperation->clientTokenLength == clientTokenLength ) &&
( strncmp( pOperation->pClientToken, pClientToken, clientTokenLength ) == 0 );
}
else
{
IotLogWarn( "Received a Jobs %s response with no client token. "
"This is possibly a response to a bad JSON document:\r\n%.*s",
_pAwsIotJobsOperationNames[ pParam->type ],
pParam->responseLength,
pParam->pResponse );
}
}
return match;
}
/*-----------------------------------------------------------*/
static void _getPendingCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( JOBS_GET_PENDING, pMessage );
}
/*-----------------------------------------------------------*/
static void _startNextCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( JOBS_START_NEXT, pMessage );
}
/*-----------------------------------------------------------*/
static void _describeCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( JOBS_DESCRIBE, pMessage );
}
/*-----------------------------------------------------------*/
static void _updateCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( JOBS_UPDATE, pMessage );
}
/*-----------------------------------------------------------*/
static void _commonOperationCallback( _jobsOperationType_t type,
IotMqttCallbackParam_t * pMessage )
{
_jobsOperation_t * pOperation = NULL;
IotLink_t * pOperationLink = NULL;
_operationMatchParams_t param = { 0 };
AwsIotStatus_t status = AWS_IOT_UNKNOWN;
uint32_t flags = 0;
/* Set operation type and response. */
param.type = type;
param.pResponse = pMessage->u.message.info.pPayload;
param.responseLength = pMessage->u.message.info.payloadLength;
/* Parse the Thing Name from the MQTT topic name. */
if( AwsIot_ParseThingName( pMessage->u.message.info.pTopicName,
pMessage->u.message.info.topicNameLength,
&( param.pThingName ),
&( param.thingNameLength ) ) == false )
{
IOT_GOTO_CLEANUP();
}
/* Lock the pending operations list for exclusive access. */
IotMutex_Lock( &( _AwsIotJobsPendingOperationsMutex ) );
/* Search for a matching pending operation. */
pOperationLink = IotListDouble_FindFirstMatch( &_AwsIotJobsPendingOperations,
NULL,
_jobsOperation_match,
&param );
/* Find and remove the first Jobs operation of the given type. */
if( pOperationLink == NULL )
{
/* Operation is not pending. It may have already been processed. Return
* without doing anything */
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
IotLogWarn( "Jobs %s callback received an unknown operation.",
_pAwsIotJobsOperationNames[ type ] );
IOT_GOTO_CLEANUP();
}
else
{
pOperation = IotLink_Container( _jobsOperation_t, pOperationLink, link );
/* Copy the flags from the Jobs operation. */
flags = pOperation->flags;
/* Remove a non-waitable operation from the pending operation list.
* Waitable operations are removed by the Wait function. */
if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == 0 )
{
IotListDouble_Remove( &( pOperation->link ) );
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
}
}
/* Parse the status from the topic name. */
status = AwsIot_ParseStatus( pMessage->u.message.info.pTopicName,
pMessage->u.message.info.topicNameLength );
switch( status )
{
case AWS_IOT_ACCEPTED:
case AWS_IOT_REJECTED:
_AwsIotJobs_ParseResponse( status,
pMessage->u.message.info.pPayload,
pMessage->u.message.info.payloadLength,
pOperation );
break;
default:
IotLogWarn( "Unknown status for %s of %.*s Jobs. Ignoring message.",
_pAwsIotJobsOperationNames[ type ],
pOperation->pSubscription->thingNameLength,
pOperation->pSubscription->pThingName );
pOperation->status = AWS_IOT_JOBS_BAD_RESPONSE;
break;
}
_notifyCompletion( pOperation );
/* For waitable operations, unlock the pending operation list mutex to allow
* the Wait function to run. */
if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
{
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
}
/* This function has no return value and no cleanup, but uses the cleanup
* label to exit on error. */
IOT_FUNCTION_CLEANUP_BEGIN();
}
/*-----------------------------------------------------------*/
static void _notifyCompletion( _jobsOperation_t * pOperation )
{
AwsIotJobsCallbackParam_t callbackParam = { .callbackType = ( AwsIotJobsCallbackType_t ) 0 };
_jobsSubscription_t * pSubscription = pOperation->pSubscription,
* pRemovedSubscription = NULL;
/* If the operation is waiting, post to its wait semaphore and return. */
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
{
IotSemaphore_Post( &( pOperation->notify.waitSemaphore ) );
}
else
{
/* Decrement the reference count. This also removes subscriptions if the
* count reaches 0. */
IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
_AwsIotJobs_DecrementReferences( pOperation,
pSubscription->pTopicBuffer,
&pRemovedSubscription );
IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
/* Set the subscription pointer used for the user callback based on whether
* a subscription was removed from the list. */
if( pRemovedSubscription != NULL )
{
pSubscription = pRemovedSubscription;
}
AwsIotJobs_Assert( pSubscription != NULL );
/* Invoke the user callback if provided. */
if( pOperation->notify.callback.function != NULL )
{
/* Set the common members of the callback parameter. */
callbackParam.callbackType = ( AwsIotJobsCallbackType_t ) pOperation->type;
callbackParam.mqttConnection = pOperation->mqttConnection;
callbackParam.pThingName = pSubscription->pThingName;
callbackParam.thingNameLength = pSubscription->thingNameLength;
callbackParam.u.operation.result = pOperation->status;
callbackParam.u.operation.reference = pOperation;
callbackParam.u.operation.pResponse = pOperation->pJobsResponse;
callbackParam.u.operation.responseLength = pOperation->jobsResponseLength;
pOperation->notify.callback.function( pOperation->notify.callback.pCallbackContext,
&callbackParam );
}
/* Destroy a removed subscription. */
if( pRemovedSubscription != NULL )
{
_AwsIotJobs_DestroySubscription( pRemovedSubscription );
}
_AwsIotJobs_DestroyOperation( pOperation );
}
}
/*-----------------------------------------------------------*/
static AwsIotJobsError_t _findSubscription( const AwsIotJobsRequestInfo_t * pRequestInfo,
char * pTopicBuffer,
uint16_t operationTopicLength,
_jobsOperation_t * pOperation,
bool * pFreeTopicBuffer )
{
AwsIotJobsError_t status = AWS_IOT_JOBS_SUCCESS;
_jobsSubscription_t * pSubscription = NULL;
/* Lookup table for Jobs operation callbacks. */
const AwsIotMqttCallbackFunction_t jobsCallbacks[ JOBS_OPERATION_COUNT ] =
{
_getPendingCallback,
_startNextCallback,
_describeCallback,
_updateCallback
};
/* Lock the subscriptions mutex for exclusive access. */
IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
/* Check for an existing subscription. This function will attempt to allocate
* a new subscription if not found. */
pSubscription = _AwsIotJobs_FindSubscription( pRequestInfo->pThingName,
pRequestInfo->thingNameLength,
true );
if( pSubscription == NULL )
{
status = AWS_IOT_JOBS_NO_MEMORY;
}
else
{
/* Ensure that the subscription Thing Name matches. */
AwsIotJobs_Assert( pSubscription != NULL );
AwsIotJobs_Assert( pSubscription->thingNameLength == pRequestInfo->thingNameLength );
AwsIotJobs_Assert( strncmp( pSubscription->pThingName,
pRequestInfo->pThingName,
pRequestInfo->thingNameLength ) == 0 );
/* Set the subscription object for the Jobs operation. */
pOperation->pSubscription = pSubscription;
/* Assign the topic buffer to the subscription to use for unsubscribing if
* the subscription has no topic buffer. */
if( pSubscription->pTopicBuffer == NULL )
{
pSubscription->pTopicBuffer = pTopicBuffer;
/* Don't free the topic buffer if it was allocated to the subscription. */
*pFreeTopicBuffer = false;
}
else
{
*pFreeTopicBuffer = true;
}
/* Increment the reference count for this Jobs operation's
* subscriptions. */
status = _AwsIotJobs_IncrementReferences( pOperation,
pTopicBuffer,
operationTopicLength,
jobsCallbacks[ pOperation->type ] );
if( status != AWS_IOT_JOBS_SUCCESS )
{
/* Failed to add subscriptions for a Jobs operation. The reference
* count was not incremented. Check if this subscription should be
* deleted. */
_AwsIotJobs_RemoveSubscription( pSubscription, NULL );
}
}
/* Unlock the Jobs subscription list mutex. */
IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
return status;
}
/*-----------------------------------------------------------*/
AwsIotJobsError_t _AwsIotJobs_CreateOperation( _jobsOperationType_t type,
const AwsIotJobsRequestInfo_t * pRequestInfo,
const _jsonRequestContents_t * pRequestContents,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
_jobsOperation_t ** pNewOperation )
{
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
size_t operationSize = sizeof( _jobsOperation_t );
_jobsOperation_t * pOperation = NULL;
IotLogDebug( "Creating operation record for Jobs %s.",
_pAwsIotJobsOperationNames[ type ] );
/* The Job ID must be saved for DESCRIBE and UPDATE operations. */
if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )
{
/* Ensure a valid Job ID is provided. */
AwsIotJobs_Assert( pRequestInfo->pJobId != NULL );
AwsIotJobs_Assert( pRequestInfo->jobIdLength > 1 );
AwsIotJobs_Assert( pRequestInfo->jobIdLength <= JOBS_MAX_ID_LENGTH );
operationSize += pRequestInfo->jobIdLength;
}
/* Allocate memory for a new Jobs operation. */
pOperation = AwsIotJobs_MallocOperation( operationSize );
if( pOperation == NULL )
{
IotLogError( "Failed to allocate memory for Jobs %s.",
_pAwsIotJobsOperationNames[ type ] );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );
}
/* Clear the operation data. */
( void ) memset( pOperation, 0x00, sizeof( _jobsOperation_t ) );
/* Set the remaining common members of the Jobs operation. */
pOperation->type = type;
pOperation->flags = flags;
pOperation->status = AWS_IOT_JOBS_STATUS_PENDING;
pOperation->mallocResponse = pRequestInfo->mallocResponse;
/* Save the Job ID for DESCRIBE and UPDATE operations. */
if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )
{
pOperation->jobIdLength = pRequestInfo->jobIdLength;
( void ) memcpy( pOperation->pJobId, pRequestInfo->pJobId, pRequestInfo->jobIdLength );
}
/* Generate a Jobs request document. */
status = _AwsIotJobs_GenerateJsonRequest( type,
pRequestInfo,
pRequestContents,
pOperation );
if( status != AWS_IOT_JOBS_SUCCESS )
{
IOT_GOTO_CLEANUP();
}
/* Check if the waitable flag is set. If it is, create a semaphore to
* wait on. */
if( ( flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
{
if( IotSemaphore_Create( &( pOperation->notify.waitSemaphore ), 0, 1 ) == false )
{
IotLogError( "Failed to create semaphore for waitable Jobs %s.",
_pAwsIotJobsOperationNames[ type ] );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_NO_MEMORY );
}
}
else
{
/* If the waitable flag isn't set but a callback is, copy the callback
* information. */
if( pCallbackInfo != NULL )
{
pOperation->notify.callback = *pCallbackInfo;
}
}
IOT_FUNCTION_CLEANUP_BEGIN();
/* Clean up on error. */
if( status != AWS_IOT_JOBS_SUCCESS )
{
if( pOperation != NULL )
{
if( pOperation->pJobsRequest != NULL )
{
AwsIotJobs_FreeString( ( void * ) ( pOperation->pJobsRequest ) );
}
AwsIotJobs_FreeOperation( pOperation );
}
}
else
{
/* Set the output parameter. */
*pNewOperation = pOperation;
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/
void _AwsIotJobs_DestroyOperation( void * pData )
{
_jobsOperation_t * pOperation = ( _jobsOperation_t * ) pData;
AwsIotJobs_Assert( pOperation != NULL );
IotLogDebug( "Destroying Jobs operation %s.",
_pAwsIotJobsOperationNames[ pOperation->type ] );
/* Check if a wait semaphore was created for this operation. */
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_WAITABLE ) == AWS_IOT_JOBS_FLAG_WAITABLE )
{
/* Destroy the wait semaphore */
IotSemaphore_Destroy( &( pOperation->notify.waitSemaphore ) );
}
/* Free any Jobs request documents. */
if( pOperation->pJobsRequest != NULL )
{
AwsIotJobs_Assert( pOperation->jobsRequestLength > 0 );
AwsIotJobs_FreeString( ( void * ) ( pOperation->pJobsRequest ) );
}
/* Free the memory used to hold operation data. */
AwsIotJobs_FreeOperation( pOperation );
}
/*-----------------------------------------------------------*/
AwsIotJobsError_t _AwsIotJobs_GenerateJobsTopic( _jobsOperationType_t type,
const AwsIotJobsRequestInfo_t * pRequestInfo,
char ** pTopicBuffer,
uint16_t * pOperationTopicLength )
{
AwsIotJobsError_t status = AWS_IOT_JOBS_SUCCESS;
AwsIotTopicInfo_t topicInfo = { 0 };
char pJobOperationName[ JOBS_LONGEST_SUFFIX_LENGTH ] = { 0 };
uint16_t operationNameLength = 0;
/* Lookup table for Jobs operation strings. */
const char * const pOperationString[ JOBS_OPERATION_COUNT ] =
{
JOBS_GET_PENDING_OPERATION_STRING, /* Jobs get pending operation. */
JOBS_START_NEXT_OPERATION_STRING, /* Jobs start next operation. */
JOBS_DESCRIBE_OPERATION_STRING, /* Jobs describe operation */
JOBS_UPDATE_OPERATION_STRING /* Jobs update operation. */
};
/* Lookup table for Jobs operation string lengths. */
const uint16_t pOperationStringLength[ JOBS_OPERATION_COUNT ] =
{
JOBS_GET_PENDING_OPERATION_STRING_LENGTH, /* Jobs get pending operation */
JOBS_START_NEXT_OPERATION_STRING_LENGTH, /* Jobs start next operation. */
JOBS_DESCRIBE_OPERATION_STRING_LENGTH, /* Jobs describe operation */
JOBS_UPDATE_OPERATION_STRING_LENGTH /* Jobs update operation. */
};
/* Ensure type is valid. */
AwsIotJobs_Assert( ( type == JOBS_GET_PENDING ) || ( type == JOBS_START_NEXT ) ||
( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) );
/* Set the members needed to generate an operation topic. */
topicInfo.pThingName = pRequestInfo->pThingName;
topicInfo.thingNameLength = pRequestInfo->thingNameLength;
topicInfo.longestSuffixLength = JOBS_LONGEST_SUFFIX_LENGTH;
topicInfo.mallocString = AwsIotJobs_MallocString;
/* Job operations that require a Job ID require additional processing to
* create an operation name with the Job ID. */
if( ( type == JOBS_DESCRIBE ) || ( type == JOBS_UPDATE ) )
{
/* Ensure Job ID length is valid. */
AwsIotJobs_Assert( pRequestInfo->jobIdLength > 0 );
AwsIotJobs_Assert( pRequestInfo->jobIdLength <= JOBS_MAX_ID_LENGTH );
/* Construct the Jobs operation name with the Job ID. */
( void ) memcpy( pJobOperationName, "/jobs/", 6 );
operationNameLength = 6;
( void ) memcpy( pJobOperationName + operationNameLength,
pRequestInfo->pJobId,
pRequestInfo->jobIdLength );
operationNameLength = ( uint16_t ) ( pRequestInfo->jobIdLength + operationNameLength );
( void ) memcpy( pJobOperationName + operationNameLength,
pOperationString[ type ],
pOperationStringLength[ type ] );
operationNameLength = ( uint16_t ) ( operationNameLength + pOperationStringLength[ type ] );
topicInfo.pOperationName = pJobOperationName;
topicInfo.operationNameLength = operationNameLength;
}
else
{
topicInfo.pOperationName = pOperationString[ type ];
topicInfo.operationNameLength = pOperationStringLength[ type ];
}
if( AwsIot_GenerateOperationTopic( &topicInfo,
pTopicBuffer,
pOperationTopicLength ) == false )
{
status = AWS_IOT_JOBS_NO_MEMORY;
}
return status;
}
/*-----------------------------------------------------------*/
AwsIotJobsError_t _AwsIotJobs_ProcessOperation( const AwsIotJobsRequestInfo_t * pRequestInfo,
_jobsOperation_t * pOperation )
{
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_STATUS_PENDING );
char * pTopicBuffer = NULL;
uint16_t operationTopicLength = 0;
bool freeTopicBuffer = true;
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
IotMqttError_t publishStatus = IOT_MQTT_STATUS_PENDING;
IotLogDebug( "Processing Jobs operation %s for Thing %.*s.",
_pAwsIotJobsOperationNames[ pOperation->type ],
pRequestInfo->thingNameLength,
pRequestInfo->pThingName );
/* Set the operation's MQTT connection. */
pOperation->mqttConnection = pRequestInfo->mqttConnection;
/* Generate the operation topic buffer. */
status = _AwsIotJobs_GenerateJobsTopic( pOperation->type,
pRequestInfo,
&pTopicBuffer,
&operationTopicLength );
if( status != AWS_IOT_JOBS_SUCCESS )
{
IotLogError( "No memory for Jobs operation topic buffer." );
IOT_GOTO_CLEANUP();
}
/* Get a subscription object for this Jobs operation. */
status = _findSubscription( pRequestInfo,
pTopicBuffer,
operationTopicLength,
pOperation,
&freeTopicBuffer );
if( status != AWS_IOT_JOBS_SUCCESS )
{
/* No subscription was found and no subscription could be allocated. */
IOT_GOTO_CLEANUP();
}
/* Set the members for PUBLISH retry. */
publishInfo.qos = pRequestInfo->qos;
publishInfo.retryLimit = pRequestInfo->retryLimit;
publishInfo.retryMs = pRequestInfo->retryMs;
/* Set the payload as the Jobs request. */
publishInfo.pPayload = pOperation->pJobsRequest;
publishInfo.payloadLength = pOperation->jobsRequestLength;
/* Set the operation topic name. */
publishInfo.pTopicName = pTopicBuffer;
publishInfo.topicNameLength = operationTopicLength;
IotLogDebug( "Jobs %s message will be published to topic %.*s",
_pAwsIotJobsOperationNames[ pOperation->type ],
publishInfo.topicNameLength,
publishInfo.pTopicName );
/* Add Jobs operation to the pending operations list. */
IotMutex_Lock( &( _AwsIotJobsPendingOperationsMutex ) );
IotListDouble_InsertHead( &( _AwsIotJobsPendingOperations ),
&( pOperation->link ) );
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
/* Publish to the Jobs topic name. */
publishStatus = IotMqtt_PublishSync( pOperation->mqttConnection,
&publishInfo,
0,
_AwsIotJobsMqttTimeoutMs );
if( publishStatus != IOT_MQTT_SUCCESS )
{
IotLogError( "Failed to publish MQTT message to %s %.*s Jobs, error %s.",
_pAwsIotJobsOperationNames[ pOperation->type ],
pRequestInfo->thingNameLength,
pRequestInfo->pThingName,
IotMqtt_strerror( publishStatus ) );
/* Convert the MQTT "NO MEMORY" error to a Jobs "NO MEMORY" error. */
if( publishStatus == IOT_MQTT_NO_MEMORY )
{
status = AWS_IOT_JOBS_NO_MEMORY;
}
else
{
status = AWS_IOT_JOBS_MQTT_ERROR;
}
/* If the "keep subscriptions" flag is not set, decrement the reference
* count. */
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )
{
IotMutex_Lock( &_AwsIotJobsSubscriptionsMutex );
_AwsIotJobs_DecrementReferences( pOperation,
pTopicBuffer,
NULL );
IotMutex_Unlock( &_AwsIotJobsSubscriptionsMutex );
}
/* Remove Jobs operation from the pending operations list. */
IotMutex_Lock( &( _AwsIotJobsPendingOperationsMutex ) );
IotListDouble_Remove( &( pOperation->link ) );
IotMutex_Unlock( &( _AwsIotJobsPendingOperationsMutex ) );
}
else
{
IotLogDebug( "Jobs %s PUBLISH message successfully sent.",
_pAwsIotJobsOperationNames[ pOperation->type ] );
}
IOT_FUNCTION_CLEANUP_BEGIN();
/* Free the topic buffer used by this function if it was not assigned to a
* subscription. */
if( ( freeTopicBuffer == true ) && ( pTopicBuffer != NULL ) )
{
AwsIotJobs_FreeString( pTopicBuffer );
}
/* Destroy the Jobs operation on failure. */
if( status != AWS_IOT_JOBS_SUCCESS )
{
_AwsIotJobs_DestroyOperation( pOperation );
}
else
{
/* Convert successful return code to "status pending", as the Jobs
* library is now waiting for a response from the service. */
status = AWS_IOT_JOBS_STATUS_PENDING;
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,169 @@
/*
* AWS IoT Jobs V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_jobs_static_memory.c
* @brief Implementation of Jobs static memory functions.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* This file should only be compiled if dynamic memory allocation is forbidden. */
#if IOT_STATIC_MEMORY_ONLY == 1
/* Standard includes. */
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
/* Static memory include. */
#include "iot_static_memory.h"
/* Jobs internal include. */
#include "private/aws_iot_jobs_internal.h"
/*-----------------------------------------------------------*/
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Provide default values for undefined configuration constants.
*/
#ifndef AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS
#define AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS ( 10 )
#endif
#ifndef AWS_IOT_JOBS_SUBSCRIPTIONS
#define AWS_IOT_JOBS_SUBSCRIPTIONS ( 2 )
#endif
/** @endcond */
/* Validate static memory configuration settings. */
#if AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS <= 0
#error "AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS cannot be 0 or negative."
#endif
#if AWS_IOT_JOBS_SUBSCRIPTIONS <= 0
#error "AWS_IOT_JOBS_SUBSCRIPTIONS cannot be 0 or negative."
#endif
/**
* @brief The size of a static memory Jobs operation.
*
* Since the pJobId member of #_jobsOperation_t is variable-length,
* the constant `JOBS_MAX_ID_LENGTH` is used for the length of
* #_jobsOperation_t.pJobId.
*/
#define JOBS_OPERATION_SIZE ( sizeof( _jobsOperation_t ) + JOBS_MAX_ID_LENGTH )
/**
* @brief The size of a static memory Jobs subscription.
*
* Since the pThingName member of #_jobsSubscription_t is variable-length,
* the constant `AWS_IOT_MAX_THING_NAME_LENGTH` is used for the length of
* #_jobsSubscription_t.pThingName.
*/
#define JOBS_SUBSCRIPTION_SIZE ( sizeof( _jobsSubscription_t ) + AWS_IOT_MAX_THING_NAME_LENGTH )
/*-----------------------------------------------------------*/
/*
* Static memory buffers and flags, allocated and zeroed at compile-time.
*/
static uint32_t _pInUseJobsOperations[ AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS ] = { 0U }; /**< @brief Jobs operation in-use flags. */
static char _pJobsOperations[ AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS ][ JOBS_OPERATION_SIZE ] = { { 0 } }; /**< @brief Jobs operations. */
static uint32_t _pInUseJobsSubscriptions[ AWS_IOT_JOBS_SUBSCRIPTIONS ] = { 0U }; /**< @brief Jobs subscription in-use flags. */
static char _pJobsSubscriptions[ AWS_IOT_JOBS_SUBSCRIPTIONS ][ JOBS_SUBSCRIPTION_SIZE ] = { { 0 } }; /**< @brief Jobs subscriptions. */
/*-----------------------------------------------------------*/
void * AwsIotJobs_MallocOperation( size_t size )
{
int32_t freeIndex = -1;
void * pNewOperation = NULL;
/* Check size argument. */
if( size <= JOBS_OPERATION_SIZE )
{
/* Find a free Jobs operation. */
freeIndex = IotStaticMemory_FindFree( _pInUseJobsOperations,
AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS );
if( freeIndex != -1 )
{
pNewOperation = &( _pJobsOperations[ freeIndex ] );
}
}
return pNewOperation;
}
/*-----------------------------------------------------------*/
void AwsIotJobs_FreeOperation( void * ptr )
{
/* Return the in-use Jobs operation. */
IotStaticMemory_ReturnInUse( ptr,
_pJobsOperations,
_pInUseJobsOperations,
AWS_IOT_JOBS_MAX_IN_PROGRESS_OPERATIONS,
JOBS_OPERATION_SIZE );
}
/*-----------------------------------------------------------*/
void * AwsIotJobs_MallocSubscription( size_t size )
{
int32_t freeIndex = -1;
void * pNewSubscription = NULL;
if( size <= JOBS_SUBSCRIPTION_SIZE )
{
/* Get the index of a free Jobs subscription. */
freeIndex = IotStaticMemory_FindFree( _pInUseJobsSubscriptions,
AWS_IOT_JOBS_SUBSCRIPTIONS );
if( freeIndex != -1 )
{
pNewSubscription = &( _pJobsSubscriptions[ freeIndex ][ 0 ] );
}
}
return pNewSubscription;
}
/*-----------------------------------------------------------*/
void AwsIotJobs_FreeSubscription( void * ptr )
{
/* Return the in-use Jobs subscription. */
IotStaticMemory_ReturnInUse( ptr,
_pJobsSubscriptions,
_pInUseJobsSubscriptions,
AWS_IOT_JOBS_SUBSCRIPTIONS,
JOBS_SUBSCRIPTION_SIZE );
}
/*-----------------------------------------------------------*/
#endif

View file

@ -0,0 +1,581 @@
/*
* AWS IoT Jobs V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_jobs_subscription.c
* @brief Implements functions for interacting with the Jobs library's
* subscription list.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* Jobs internal include. */
#include "private/aws_iot_jobs_internal.h"
/* Error handling include. */
#include "iot_error.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
/* MQTT include. */
#include "iot_mqtt.h"
/*-----------------------------------------------------------*/
/**
* @brief Match two #_jobsSubscription_t by Thing Name.
*
* @param[in] pSubscriptionLink Pointer to the link member of a #_jobsSubscription_t
* containing the Thing Name to check.
* @param[in] pMatch Pointer to a `AwsIotThingName_t`.
*
* @return `true` if the Thing Names match; `false` otherwise.
*/
static bool _jobsSubscription_match( const IotLink_t * pSubscriptionLink,
void * pMatch );
/*-----------------------------------------------------------*/
/**
* @brief List of active Jobs subscriptions objects.
*/
IotListDouble_t _AwsIotJobsSubscriptions = { 0 };
/**
* @brief Protects #_AwsIotJobsSubscriptions from concurrent access.
*/
IotMutex_t _AwsIotJobsSubscriptionsMutex;
/*-----------------------------------------------------------*/
static bool _jobsSubscription_match( const IotLink_t * pSubscriptionLink,
void * pMatch )
{
bool match = false;
/* Because this function is called from a container function, the given link
* must never be NULL. */
AwsIotJobs_Assert( pSubscriptionLink != NULL );
const _jobsSubscription_t * pSubscription = IotLink_Container( _jobsSubscription_t,
pSubscriptionLink,
link );
const AwsIotThingName_t * pThingName = ( AwsIotThingName_t * ) pMatch;
if( pThingName->thingNameLength == pSubscription->thingNameLength )
{
/* Check for matching Thing Names. */
match = ( strncmp( pThingName->pThingName,
pSubscription->pThingName,
pThingName->thingNameLength ) == 0 );
}
return match;
}
/*-----------------------------------------------------------*/
_jobsSubscription_t * _AwsIotJobs_FindSubscription( const char * pThingName,
size_t thingNameLength,
bool createIfNotFound )
{
_jobsSubscription_t * pSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
AwsIotThingName_t thingName = { 0 };
thingName.pThingName = pThingName;
thingName.thingNameLength = thingNameLength;
/* Search the list for an existing subscription for Thing Name. */
pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotJobsSubscriptions ),
NULL,
_jobsSubscription_match,
&thingName );
/* Check if a subscription was found. */
if( pSubscriptionLink == NULL )
{
if( createIfNotFound == true )
{
/* No subscription found. Allocate a new subscription. */
pSubscription = AwsIotJobs_MallocSubscription( sizeof( _jobsSubscription_t ) + thingNameLength );
if( pSubscription != NULL )
{
/* Clear the new subscription. */
( void ) memset( pSubscription, 0x00, sizeof( _jobsSubscription_t ) + thingNameLength );
/* Set the Thing Name length and copy the Thing Name into the new subscription. */
pSubscription->thingNameLength = thingNameLength;
( void ) memcpy( pSubscription->pThingName, pThingName, thingNameLength );
/* Add the new subscription to the subscription list. */
IotListDouble_InsertHead( &( _AwsIotJobsSubscriptions ),
&( pSubscription->link ) );
IotLogDebug( "Created new Jobs subscriptions object for %.*s.",
thingNameLength,
pThingName );
}
else
{
IotLogError( "Failed to allocate memory for %.*s Jobs subscriptions.",
thingNameLength,
pThingName );
}
}
}
else
{
IotLogDebug( "Found existing Jobs subscriptions object for %.*s.",
thingNameLength,
pThingName );
pSubscription = IotLink_Container( _jobsSubscription_t, pSubscriptionLink, link );
}
return pSubscription;
}
/*-----------------------------------------------------------*/
void _AwsIotJobs_RemoveSubscription( _jobsSubscription_t * pSubscription,
_jobsSubscription_t ** pRemovedSubscription )
{
IOT_FUNCTION_ENTRY( bool, true );
int32_t i = 0, callbackIndex = 0;
IotLogDebug( "Checking if subscription object for %.*s can be removed.",
pSubscription->thingNameLength,
pSubscription->pThingName );
/* Check for active operations. If any Jobs operation's subscription
* reference count is not 0, then the subscription cannot be removed. */
for( i = 0; i < JOBS_OPERATION_COUNT; i++ )
{
if( pSubscription->operationReferences[ i ] > 0 )
{
IotLogDebug( "Reference count %ld for %.*s subscription object. "
"Subscription cannot be removed yet.",
( long int ) pSubscription->operationReferences[ i ],
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else if( pSubscription->operationReferences[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
IotLogDebug( "Subscription object for %.*s has persistent subscriptions. "
"Subscription will not be removed.",
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_SET_AND_GOTO_CLEANUP( false );
}
}
/* Check for active subscriptions. If any Jobs callbacks are active, then the
* subscription cannot be removed. */
if( pSubscription->callbackReferences > 0 )
{
IotLogDebug( "Notify callbacks are using %.*s subscription object. "
"Subscription cannot be removed yet.",
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_SET_AND_GOTO_CLEANUP( false );
}
for( i = 0; i < JOBS_CALLBACK_COUNT; i++ )
{
for( callbackIndex = 0; callbackIndex < AWS_IOT_JOBS_NOTIFY_CALLBACKS; callbackIndex++ )
{
if( pSubscription->callbacks[ i ][ callbackIndex ].function != NULL )
{
IotLogDebug( "Found active Jobs %s callback for %.*s subscription object. "
"Subscription cannot be removed yet.",
_pAwsIotJobsCallbackNames[ i ],
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_SET_AND_GOTO_CLEANUP( false );
}
}
}
/* Remove the subscription if unused. */
IOT_FUNCTION_CLEANUP_BEGIN();
if( status == true )
{
/* No Jobs operation subscription references or active Jobs callbacks.
* Remove the subscription object. */
IotListDouble_Remove( &( pSubscription->link ) );
IotLogDebug( "Removed subscription object for %.*s.",
pSubscription->thingNameLength,
pSubscription->pThingName );
/* If the caller requested the removed subscription, set the output parameter.
* Otherwise, free the memory used by the subscription. */
if( pRemovedSubscription != NULL )
{
*pRemovedSubscription = pSubscription;
}
else
{
_AwsIotJobs_DestroySubscription( pSubscription );
}
}
}
/*-----------------------------------------------------------*/
void _AwsIotJobs_DestroySubscription( void * pData )
{
_jobsSubscription_t * pSubscription = ( _jobsSubscription_t * ) pData;
/* Free the topic buffer if allocated. */
if( pSubscription->pTopicBuffer != NULL )
{
AwsIotJobs_FreeString( pSubscription->pTopicBuffer );
}
/* Free memory used by subscription. */
AwsIotJobs_FreeSubscription( pSubscription );
}
/*-----------------------------------------------------------*/
AwsIotJobsError_t _AwsIotJobs_IncrementReferences( _jobsOperation_t * pOperation,
char * pTopicBuffer,
uint16_t operationTopicLength,
AwsIotMqttCallbackFunction_t callback )
{
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
const _jobsOperationType_t type = pOperation->type;
_jobsSubscription_t * pSubscription = pOperation->pSubscription;
IotMqttError_t subscriptionStatus = IOT_MQTT_STATUS_PENDING;
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
/* Do nothing if this operation has persistent subscriptions. */
if( pSubscription->operationReferences[ type ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
IotLogDebug( "Jobs %s for %.*s has persistent subscriptions. Reference "
"count will not be incremented.",
_pAwsIotJobsOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_GOTO_CLEANUP();
}
/* When persistent subscriptions are not present, the reference count must
* not be negative. */
AwsIotJobs_Assert( pSubscription->operationReferences[ type ] >= 0 );
/* Check if there are any existing references for this operation. */
if( pSubscription->operationReferences[ type ] == 0 )
{
/* Set the parameters needed to add subscriptions. */
subscriptionInfo.mqttConnection = pOperation->mqttConnection;
subscriptionInfo.callbackFunction = callback;
subscriptionInfo.timeout = _AwsIotJobsMqttTimeoutMs;
subscriptionInfo.pTopicFilterBase = pTopicBuffer;
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
subscriptionStatus = AwsIot_ModifySubscriptions( IotMqtt_SubscribeSync,
&subscriptionInfo );
/* Convert MQTT return code to Jobs return code. */
switch( subscriptionStatus )
{
case IOT_MQTT_SUCCESS:
status = AWS_IOT_JOBS_SUCCESS;
break;
case IOT_MQTT_NO_MEMORY:
status = AWS_IOT_JOBS_NO_MEMORY;
break;
default:
status = AWS_IOT_JOBS_MQTT_ERROR;
break;
}
if( status != AWS_IOT_JOBS_SUCCESS )
{
IOT_GOTO_CLEANUP();
}
}
/* Increment the number of subscription references for this operation when
* the keep subscriptions flag is not set. */
if( ( pOperation->flags & AWS_IOT_JOBS_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )
{
( pSubscription->operationReferences[ type ] )++;
IotLogDebug( "Jobs %s subscriptions for %.*s now has count %d.",
_pAwsIotJobsOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName,
pSubscription->operationReferences[ type ] );
}
/* Otherwise, set the persistent subscriptions flag. */
else
{
pSubscription->operationReferences[ type ] = AWS_IOT_PERSISTENT_SUBSCRIPTION;
IotLogDebug( "Set persistent subscriptions flag for Jobs %s of %.*s.",
_pAwsIotJobsOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName );
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
void _AwsIotJobs_DecrementReferences( _jobsOperation_t * pOperation,
char * pTopicBuffer,
_jobsSubscription_t ** pRemovedSubscription )
{
const _jobsOperationType_t type = pOperation->type;
_jobsSubscription_t * pSubscription = pOperation->pSubscription;
uint16_t operationTopicLength = 0;
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
AwsIotJobsRequestInfo_t requestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;
/* Do nothing if this Jobs operation has persistent subscriptions. */
if( pSubscription->operationReferences[ type ] != AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
/* Decrement the number of subscription references for this operation.
* Ensure that it's positive. */
( pSubscription->operationReferences[ type ] )--;
AwsIotJobs_Assert( pSubscription->operationReferences[ type ] >= 0 );
/* Check if the number of references has reached 0. */
if( pSubscription->operationReferences[ type ] == 0 )
{
IotLogDebug( "Reference count for %.*s %s is 0. Unsubscribing.",
pSubscription->thingNameLength,
pSubscription->pThingName,
_pAwsIotJobsOperationNames[ type ] );
/* Subscription must have a topic buffer. */
AwsIotJobs_Assert( pSubscription->pTopicBuffer != NULL );
/* Set the parameters needed to generate a Jobs topic. */
requestInfo.pThingName = pSubscription->pThingName;
requestInfo.thingNameLength = pSubscription->thingNameLength;
requestInfo.pJobId = pOperation->pJobId;
requestInfo.jobIdLength = pOperation->jobIdLength;
/* Generate the prefix of the Jobs topic. This function will not
* fail when given a buffer. */
( void ) _AwsIotJobs_GenerateJobsTopic( ( _jobsOperationType_t ) type,
&requestInfo,
&( pSubscription->pTopicBuffer ),
&operationTopicLength );
/* Set the parameters needed to remove subscriptions. */
subscriptionInfo.mqttConnection = pOperation->mqttConnection;
subscriptionInfo.timeout = _AwsIotJobsMqttTimeoutMs;
subscriptionInfo.pTopicFilterBase = pTopicBuffer;
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
( void ) AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,
&subscriptionInfo );
}
/* Check if this subscription should be deleted. */
_AwsIotJobs_RemoveSubscription( pSubscription,
pRemovedSubscription );
}
else
{
IotLogDebug( "Jobs %s for %.*s has persistent subscriptions. Reference "
"count will not be decremented.",
_pAwsIotJobsOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName );
}
}
/*-----------------------------------------------------------*/
AwsIotJobsError_t AwsIotJobs_RemovePersistentSubscriptions( const AwsIotJobsRequestInfo_t * pRequestInfo,
uint32_t flags )
{
IOT_FUNCTION_ENTRY( AwsIotJobsError_t, AWS_IOT_JOBS_SUCCESS );
int32_t i = 0;
uint16_t operationTopicLength = 0;
IotMqttError_t unsubscribeStatus = IOT_MQTT_STATUS_PENDING;
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
_jobsSubscription_t * pSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
AwsIotThingName_t thingName = { 0 };
thingName.pThingName = pRequestInfo->pThingName;
thingName.thingNameLength = pRequestInfo->thingNameLength;
IotLogInfo( "Removing persistent subscriptions for %.*s.",
pRequestInfo->thingNameLength,
pRequestInfo->pThingName );
/* Check parameters. */
if( pRequestInfo->mqttConnection == IOT_MQTT_CONNECTION_INITIALIZER )
{
IotLogError( "MQTT connection is not initialized." );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
}
if( AwsIot_ValidateThingName( pRequestInfo->pThingName,
pRequestInfo->thingNameLength ) == false )
{
IotLogError( "Thing Name is not valid." );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
}
if( ( ( flags & AWS_IOT_JOBS_FLAG_REMOVE_DESCRIBE_SUBSCRIPTIONS ) != 0 ) ||
( ( flags & AWS_IOT_JOBS_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS ) != 0 ) )
{
if( ( pRequestInfo->pJobId == NULL ) || ( pRequestInfo->jobIdLength == 0 ) )
{
IotLogError( "Job ID must be set." );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
}
if( pRequestInfo->jobIdLength > JOBS_MAX_ID_LENGTH )
{
IotLogError( "Job ID cannot be longer than %d.",
JOBS_MAX_ID_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_JOBS_BAD_PARAMETER );
}
}
IotMutex_Lock( &( _AwsIotJobsSubscriptionsMutex ) );
/* Search the list for an existing subscription for Thing Name. */
pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotJobsSubscriptions ),
NULL,
_jobsSubscription_match,
&thingName );
if( pSubscriptionLink != NULL )
{
IotLogDebug( "Found subscription object for %.*s. Checking for persistent "
"subscriptions to remove.",
pRequestInfo->thingNameLength,
pRequestInfo->pThingName );
pSubscription = IotLink_Container( _jobsSubscription_t, pSubscriptionLink, link );
for( i = 0; i < JOBS_OPERATION_COUNT; i++ )
{
if( ( flags & ( 0x1UL << i ) ) != 0 )
{
IotLogDebug( "Removing %.*s %s persistent subscriptions.",
pRequestInfo->thingNameLength,
pRequestInfo->pThingName,
_pAwsIotJobsOperationNames[ i ] );
/* Subscription must have a topic buffer. */
AwsIotJobs_Assert( pSubscription->pTopicBuffer != NULL );
if( pSubscription->operationReferences[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
/* Generate the prefix of the Jobs topic. This function will not
* fail when given a buffer. */
( void ) _AwsIotJobs_GenerateJobsTopic( ( _jobsOperationType_t ) i,
pRequestInfo,
&( pSubscription->pTopicBuffer ),
&operationTopicLength );
/* Set the parameters needed to remove subscriptions. */
subscriptionInfo.mqttConnection = pRequestInfo->mqttConnection;
subscriptionInfo.timeout = _AwsIotJobsMqttTimeoutMs;
subscriptionInfo.pTopicFilterBase = pSubscription->pTopicBuffer;
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
unsubscribeStatus = AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,
&subscriptionInfo );
/* Convert MQTT return code to Shadow return code. */
switch( unsubscribeStatus )
{
case IOT_MQTT_SUCCESS:
status = AWS_IOT_JOBS_SUCCESS;
break;
case IOT_MQTT_NO_MEMORY:
status = AWS_IOT_JOBS_NO_MEMORY;
break;
default:
status = AWS_IOT_JOBS_MQTT_ERROR;
break;
}
if( status != AWS_IOT_JOBS_SUCCESS )
{
break;
}
/* Clear the persistent subscriptions flag and check if the
* subscription can be removed. */
pSubscription->operationReferences[ i ] = 0;
_AwsIotJobs_RemoveSubscription( pSubscription, NULL );
}
else
{
IotLogDebug( "%.*s %s does not have persistent subscriptions.",
pRequestInfo->thingNameLength,
pRequestInfo->pThingName,
_pAwsIotJobsOperationNames[ i ] );
}
}
}
}
else
{
IotLogWarn( "No subscription object found for %.*s",
pRequestInfo->thingNameLength,
pRequestInfo->pThingName );
}
IotMutex_Unlock( &( _AwsIotJobsSubscriptionsMutex ) );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,628 @@
/*
* AWS IoT Jobs V1.0.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_jobs_internal.h
* @brief Internal header of Jobs library. This header should not be included in
* typical application code.
*/
#ifndef AWS_IOT_JOBS_INTERNAL_H_
#define AWS_IOT_JOBS_INTERNAL_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Linear containers (lists and queues) include. */
#include "iot_linear_containers.h"
/* Jobs include. */
#include "aws_iot_jobs.h"
/* AWS IoT include. */
#include "aws_iot.h"
/**
* @def AwsIotJobs_Assert( expression )
* @brief Assertion macro for the Jobs library.
*
* Set @ref AWS_IOT_JOBS_ENABLE_ASSERTS to `1` to enable assertions in the Jobs
* library.
*
* @param[in] expression Expression to be evaluated.
*/
#if AWS_IOT_JOBS_ENABLE_ASSERTS == 1
#ifndef AwsIotJobs_Assert
#ifdef Iot_DefaultAssert
#define AwsIotJobs_Assert( expression ) Iot_DefaultAssert( expression )
#else
#error "Asserts are enabled for Jobs, but AwsIotJobs_Assert is not defined"
#endif
#endif
#else
#define AwsIotJobs_Assert( expression )
#endif
/* Configure logs for Jobs functions. */
#ifdef AWS_IOT_LOG_LEVEL_JOBS
#define LIBRARY_LOG_LEVEL AWS_IOT_LOG_LEVEL_JOBS
#else
#ifdef IOT_LOG_LEVEL_GLOBAL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
#else
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
#endif
#endif
#define LIBRARY_LOG_NAME ( "Jobs" )
#include "iot_logging_setup.h"
/*
* Provide default values for undefined memory allocation functions based on
* the usage of dynamic memory allocation.
*/
#if IOT_STATIC_MEMORY_ONLY == 1
#include "iot_static_memory.h"
/**
* @brief Allocate a #_jobsOperation_t. This function should have the same
* signature as [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
void * AwsIotJobs_MallocOperation( size_t size );
/**
* @brief Free a #_jobsOperation_t. This function should have the same
* signature as [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void AwsIotJobs_FreeOperation( void * ptr );
/**
* @brief Allocate a buffer for a short string, used for topic names or client
* tokens. This function should have the same signature as [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
#define AwsIotJobs_MallocString Iot_MallocMessageBuffer
/**
* @brief Free a string. This function should have the same signature as
* [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
#define AwsIotJobs_FreeString Iot_FreeMessageBuffer
/**
* @brief Allocate a #_jobsSubscription_t. This function should have the
* same signature as [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
void * AwsIotJobs_MallocSubscription( size_t size );
/**
* @brief Free a #_jobsSubscription_t. This function should have the same
* signature as [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void AwsIotJobs_FreeSubscription( void * ptr );
#else /* if IOT_STATIC_MEMORY_ONLY == 1 */
#ifndef AwsIotJobs_MallocOperation
#ifdef Iot_DefaultMalloc
#define AwsIotJobs_MallocOperation Iot_DefaultMalloc
#else
#error "No malloc function defined for AwsIotJobs_MallocOperation"
#endif
#endif
#ifndef AwsIotJobs_FreeOperation
#ifdef Iot_DefaultFree
#define AwsIotJobs_FreeOperation Iot_DefaultFree
#else
#error "No free function defined for AwsIotJobs_FreeOperation"
#endif
#endif
#ifndef AwsIotJobs_MallocString
#ifdef Iot_DefaultMalloc
#define AwsIotJobs_MallocString Iot_DefaultMalloc
#else
#error "No malloc function defined for AwsIotJobs_MallocString"
#endif
#endif
#ifndef AwsIotJobs_FreeString
#ifdef Iot_DefaultFree
#define AwsIotJobs_FreeString Iot_DefaultFree
#else
#error "No free function defined for AwsIotJobs_FreeString"
#endif
#endif
#ifndef AwsIotJobs_MallocSubscription
#ifdef Iot_DefaultMalloc
#define AwsIotJobs_MallocSubscription Iot_DefaultMalloc
#else
#error "No malloc function defined for AwsIotJobs_MallocSubscription"
#endif
#endif
#ifndef AwsIotJobs_FreeSubscription
#ifdef Iot_DefaultFree
#define AwsIotJobs_FreeSubscription Iot_DefaultFree
#else
#error "No free function defined for AwsIotJobs_FreeSubscription"
#endif
#endif
#endif /* if IOT_STATIC_MEMORY_ONLY == 1 */
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Provide default values for undefined configuration constants.
*/
#ifndef AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS
#define AWS_IOT_JOBS_DEFAULT_MQTT_TIMEOUT_MS ( 5000 )
#endif
#ifndef AWS_IOT_JOBS_NOTIFY_CALLBACKS
#define AWS_IOT_JOBS_NOTIFY_CALLBACKS ( 1 )
#endif
/** @endcond */
/**
* @brief The number of currently available Jobs operations.
*
* The 4 Jobs operations are GET PENDING, START NEXT, DESCRIBE, and UPDATE.
*/
#define JOBS_OPERATION_COUNT ( 4 )
/**
* @brief The number of currently available Jobs callbacks.
*
* The 2 Jobs callbacks are `jobs/notify` (AKA "Notify Pending") and
* `/jobs/notify-next` (AKA "Notify Next").
*/
#define JOBS_CALLBACK_COUNT ( 2 )
/**
* @brief The string representing a Jobs GET PENDING operation in a Jobs MQTT topic.
*/
#define JOBS_GET_PENDING_OPERATION_STRING "/jobs/get"
/**
* @brief The length of #JOBS_GET_PENDING_OPERATION_STRING.
*/
#define JOBS_GET_PENDING_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_GET_PENDING_OPERATION_STRING ) - 1 ) )
/**
* @brief The string representing a Jobs START NEXT operation in a Jobs MQTT topic.
*/
#define JOBS_START_NEXT_OPERATION_STRING "/jobs/start-next"
/**
* @brief The length of #JOBS_START_NEXT_OPERATION_STRING.
*/
#define JOBS_START_NEXT_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_START_NEXT_OPERATION_STRING ) - 1 ) )
/**
* @brief The string representing a Jobs DESCRIBE operation in a Jobs MQTT topic.
*
* This string should be placed after a Job ID.
*/
#define JOBS_DESCRIBE_OPERATION_STRING "/get"
/**
* @brief The length of #JOBS_DESCRIBE_OPERATION_STRING.
*/
#define JOBS_DESCRIBE_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_DESCRIBE_OPERATION_STRING ) - 1 ) )
/**
* @brief The string representing a Jobs UPDATE operation in a Jobs MQTT topic.
*
* This string should be placed after a Job ID.
*/
#define JOBS_UPDATE_OPERATION_STRING "/update"
/**
* @brief The length of #JOBS_UPDATE_OPERATION_STRING.
*
* This length excludes the length of the placeholder %s.
*/
#define JOBS_UPDATE_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_UPDATE_OPERATION_STRING ) - 1 ) )
/**
* @brief The string representing the Jobs MQTT topic for receiving notifications
* of pending Jobs.
*/
#define JOBS_NOTIFY_PENDING_CALLBACK_STRING "/jobs/notify"
/**
* @brief The length of #JOBS_NOTIFY_PENDING_CALLBACK_STRING.
*/
#define JOBS_NOTIFY_PENDING_CALLBACK_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_NOTIFY_PENDING_CALLBACK_STRING ) - 1 ) )
/**
* @brief The string representing the Jobs MQTT topic for receiving notifications
* of the next pending Job.
*/
#define JOBS_NOTIFY_NEXT_CALLBACK_STRING "/jobs/notify-next"
/**
* @brief The length of #JOBS_NOTIFY_NEXT_CALLBACK_STRING.
*/
#define JOBS_NOTIFY_NEXT_CALLBACK_STRING_LENGTH ( ( uint16_t ) ( sizeof( JOBS_NOTIFY_NEXT_CALLBACK_STRING ) - 1 ) )
/**
* @brief The maximum length of a Job ID, per AWS IoT Service Limits.
*
* See https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits
*/
#define JOBS_MAX_ID_LENGTH ( 64 )
/**
* @brief The maximum value of the Jobs step timeout, per AWS IoT Service Limits.
*
* See https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits
*/
#define JOBS_MAX_TIMEOUT ( 10080 )
/**
* @brief A limit on the maximum length of a Jobs status details, per AWS IoT
* Service Limits.
*
* See https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits
*
* This is actually the limit on the length of an entire Jobs document; but the
* status details must also not exceed this length,
*/
#define JOBS_MAX_STATUS_DETAILS_LENGTH ( 32768 )
/**
* @brief The length of the longest Jobs topic suffix.
*
* This is the length of the longest Job ID, plus the length of the "UPDATE"
* operation suffix, plus the length of "/jobs/".
*/
#define JOBS_LONGEST_SUFFIX_LENGTH ( JOBS_MAX_ID_LENGTH + JOBS_UPDATE_OPERATION_STRING_LENGTH + 6 )
/*------------------------ Jobs internal data types -------------------------*/
/**
* @brief Enumerations representing each of the Jobs library's API functions.
*/
typedef enum _jobsOperationType
{
/* Jobs operations. */
JOBS_GET_PENDING = 0, /**< @ref jobs_function_getpendingasync */
JOBS_START_NEXT = 1, /**< @ref jobs_function_startnextasync */
JOBS_DESCRIBE = 2, /**< @ref jobs_function_describeasync */
JOBS_UPDATE = 3, /**< @ref jobs_function_updateasync */
/* Jobs callbacks. */
SET_NOTIFY_PENDING_CALLBACK = 4, /**< @ref jobs_function_setnotifypendingcallback */
SET_NOTIFY_NEXT_CALLBACK = 5 /**< @ref jobs_function_setnotifynextcallback */
} _jobsOperationType_t;
/**
* @brief Enumerations representing each of the Jobs callback functions.
*/
typedef enum _jobsCallbackType
{
NOTIFY_PENDING_CALLBACK = 0, /**< Pending Job notification callback. */
NOTIFY_NEXT_CALLBACK = 1 /**< Next Job notification callback. */
} _jobsCallbackType_t;
/**
* @brief Parameter to #_AwsIotJobs_GenerateJsonRequest.
*/
typedef union _jsonRequestContents
{
const AwsIotJobsUpdateInfo_t * pUpdateInfo; /**< @brief Valid for #JOBS_START_NEXT and #JOBS_UPDATE. */
struct
{
int32_t executionNumber; /**< @brief Execution number. */
bool includeJobDocument; /**< @brief Whether the response should include the Job document. */
} describe; /**< @brief Valid for #JOBS_DESCRIBE. */
} _jsonRequestContents_t;
/**
* @brief Represents a Jobs subscriptions object.
*
* These structures are stored in a list.
*/
typedef struct _jobsSubscription
{
IotLink_t link; /**< @brief List link member. */
int32_t operationReferences[ JOBS_OPERATION_COUNT ]; /**< @brief Reference counters for Jobs operation topics. */
int32_t callbackReferences; /**< @brief Reference counter for Jobs callbacks. */
/** @brief Jobs callbacks for this Thing. */
AwsIotJobsCallbackInfo_t callbacks[ JOBS_CALLBACK_COUNT ][ AWS_IOT_JOBS_NOTIFY_CALLBACKS ];
/**
* @brief Buffer allocated for removing Jobs topics.
*
* This buffer is pre-allocated to ensure that memory is available when
* unsubscribing.
*/
char * pTopicBuffer;
size_t thingNameLength; /**< @brief Length of Thing Name. */
char pThingName[]; /**< @brief Thing Name associated with this subscriptions object. */
} _jobsSubscription_t;
/**
* @brief Internal structure representing a single Jobs operation.
*
* A list of these structures keeps track of all in-progress Jobs operations.
*/
typedef struct _jobsOperation
{
IotLink_t link; /**< @brief List link member. */
/* Basic operation information. */
_jobsOperationType_t type; /**< @brief Operation type. */
uint32_t flags; /**< @brief Flags passed to operation API function. */
AwsIotJobsError_t status; /**< @brief Status of operation. */
IotMqttConnection_t mqttConnection; /**< @brief MQTT connection associated with this operation. */
_jobsSubscription_t * pSubscription; /**< @brief Jobs subscriptions object associated with this operation. */
/* Jobs request information. */
const char * pJobsRequest; /**< @brief JSON document to send to the Jobs service. */
size_t jobsRequestLength; /**< @brief Length of #_jobsOperation_t.pJobsRequest. */
const char * pClientToken; /**< @brief Client token sent with request. */
size_t clientTokenLength; /**< @brief Length of #_jobsOperation_t.pClientToken. */
/* Jobs response information. */
const char * pJobsResponse; /**< @brief Response received from the Jobs service. */
size_t jobsResponseLength; /**< @brief Length of #_jobsOperation_t.pJobsResponse. */
/**
* @brief Function to allocate memory for an incoming Jobs response.
*
* Only used when the flag #AWS_IOT_JOBS_FLAG_WAITABLE is set.
*/
void * ( *mallocResponse )( size_t );
/* How to notify of an operation's completion. */
union
{
IotSemaphore_t waitSemaphore; /**< @brief Semaphore to be used with @ref jobs_function_wait. */
AwsIotJobsCallbackInfo_t callback; /**< @brief User-provided callback function and parameter. */
} notify; /**< @brief How to notify of an operation's completion. */
size_t jobIdLength; /**< @brief Length of #_jobsOperation_t.pJobId. */
char pJobId[]; /**< @brief Job ID, saved for DESCRIBE and UPDATE operations. */
} _jobsOperation_t;
/* Declarations of names printed in logs. */
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
extern const char * const _pAwsIotJobsOperationNames[];
extern const char * const _pAwsIotJobsCallbackNames[];
#endif
/* Declarations of variables for internal Jobs files. */
extern uint32_t _AwsIotJobsMqttTimeoutMs;
extern IotListDouble_t _AwsIotJobsPendingOperations;
extern IotListDouble_t _AwsIotJobsSubscriptions;
extern IotMutex_t _AwsIotJobsPendingOperationsMutex;
extern IotMutex_t _AwsIotJobsSubscriptionsMutex;
/*------------------------ Jobs operation functions -------------------------*/
/**
* @brief Create a record for a new in-progress Jobs operation.
*
* @param[in] type The type of Jobs operation for the request.
* @param[in] pRequestInfo Common Jobs request parameters.
* @param[in] pRequestContents Additional values to place in the JSON document,
* depending on `type`.
* @param[in] flags Flags variables passed to a user-facing Jobs function.
* @param[in] pCallbackInfo User-provided callback function and parameter.
* @param[out] pNewOperation Set to point to the new operation on success.
*
* @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY
*/
AwsIotJobsError_t _AwsIotJobs_CreateOperation( _jobsOperationType_t type,
const AwsIotJobsRequestInfo_t * pRequestInfo,
const _jsonRequestContents_t * pRequestContents,
uint32_t flags,
const AwsIotJobsCallbackInfo_t * pCallbackInfo,
_jobsOperation_t ** pNewOperation );
/**
* @brief Free resources used to record a Jobs operation. This is called when
* the operation completes.
*
* @param[in] pData The operation which completed. This parameter is of type
* `void*` to match the signature of [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void _AwsIotJobs_DestroyOperation( void * pData );
/**
* @brief Fill a buffer with a Jobs topic.
*
* @param[in] type One of: GET PENDING, START NEXT, DESCRIBE, or UPDATE.
* @param[in] pRequestInfo Common Jobs request parameters.
* @param[out] pTopicBuffer Address of the buffer for the Jobs topic. If the
* pointer at this address is `NULL`, this function will allocate a new buffer;
* otherwise, it will use the provided buffer.
* @param[out] pOperationTopicLength Length of the Jobs operation topic (excluding
* any suffix) placed in `pTopicBuffer`.
*
* @warning This function does not check the length of `pTopicBuffer`! Any provided
* buffer must be large enough to accommodate the full Jobs topic, plus
* #JOBS_LONGEST_SUFFIX_LENGTH.
*
* @return #AWS_IOT_JOBS_SUCCESS or #AWS_IOT_JOBS_NO_MEMORY. This function
* will not return #AWS_IOT_JOBS_NO_MEMORY when a buffer is provided.
*/
AwsIotJobsError_t _AwsIotJobs_GenerateJobsTopic( _jobsOperationType_t type,
const AwsIotJobsRequestInfo_t * pRequestInfo,
char ** pTopicBuffer,
uint16_t * pOperationTopicLength );
/**
* @brief Process a Jobs operation by sending the necessary MQTT packets.
*
* @param[in] pRequestInfo Common Jobs request parameters.
* @param[in] pOperation Operation data to process.
*
* @return #AWS_IOT_JOBS_STATUS_PENDING on success. On error, one of
* #AWS_IOT_JOBS_NO_MEMORY or #AWS_IOT_JOBS_MQTT_ERROR.
*/
AwsIotJobsError_t _AwsIotJobs_ProcessOperation( const AwsIotJobsRequestInfo_t * pRequestInfo,
_jobsOperation_t * pOperation );
/*----------------------- Jobs subscription functions -----------------------*/
/**
* @brief Find a Jobs subscription object. May create a new subscription object
* and adds it to the subscription list if not found.
*
* @param[in] pThingName Thing Name in the subscription object.
* @param[in] thingNameLength Length of `pThingName`.
* @param[in] createIfNotFound If `true`, attempt to create a new subscription
* object if no match is found.
*
* @return Pointer to a Jobs subscription object, either found or newly
* allocated. Returns `NULL` if no subscription object is found and a new
* subscription object could not be allocated.
*
* @note This function should be called with the subscription list mutex locked.
*/
_jobsSubscription_t * _AwsIotJobs_FindSubscription( const char * pThingName,
size_t thingNameLength,
bool createIfNotFound );
/**
* @brief Remove a Jobs subscription object from the subscription list if
* unreferenced.
*
* @param[in] pSubscription Subscription object to check. If this object has no
* active references, it is removed from the subscription list.
* @param[out] pRemovedSubscription Removed subscription object, if any. Optional;
* pass `NULL` to ignore. If not `NULL`, this parameter will be set to the removed
* subscription and that subscription will not be destroyed.
*
* @note This function should be called with the subscription list mutex locked.
*/
void _AwsIotJobs_RemoveSubscription( _jobsSubscription_t * pSubscription,
_jobsSubscription_t ** pRemovedSubscription );
/**
* @brief Free resources used for a Jobs subscription object.
*
* @param[in] pData The subscription object to destroy. This parameter is of type
* `void*` to match the signature of [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void _AwsIotJobs_DestroySubscription( void * pData );
/**
* @brief Increment the reference count of a Jobs subscriptions object.
*
* Also adds MQTT subscriptions if necessary.
*
* @param[in] pOperation The operation for which the reference count should be
* incremented.
* @param[in] pTopicBuffer Topic buffer containing the operation topic, used if
* subscriptions need to be added.
* @param[in] operationTopicLength The length of the operation topic in `pTopicBuffer`.
* @param[in] callback MQTT callback function for when this operation completes.
*
* @warning This function does not check the length of `pTopicBuffer`! Any provided
* buffer must already contain the Jobs operation topic, plus enough space for the
* status suffix.
*
* @return #AWS_IOT_JOBS_SUCCESS on success. On error, one of
* #AWS_IOT_JOBS_NO_MEMORY or #AWS_IOT_JOBS_MQTT_ERROR.
*
* @note This function should be called with the subscription list mutex locked.
*/
AwsIotJobsError_t _AwsIotJobs_IncrementReferences( _jobsOperation_t * pOperation,
char * pTopicBuffer,
uint16_t operationTopicLength,
AwsIotMqttCallbackFunction_t callback );
/**
* @brief Decrement the reference count of a Jobs subscriptions object.
*
* Also removed MQTT subscriptions and deletes the subscription object if necessary.
*
* @param[in] pOperation The operation for which the reference count should be
* decremented.
* @param[in] pTopicBuffer Topic buffer containing the operation topic, used if
* subscriptions need to be removed.
* @param[out] pRemovedSubscription Set to point to a removed subscription.
* Optional; pass `NULL` to ignore. If not `NULL`, this function will not destroy
* a removed subscription.
*
* @warning This function does not check the length of `pTopicBuffer`! Any provided
* buffer must be large enough to accommodate the full Jobs topic, plus
* #JOBS_LONGEST_SUFFIX_LENGTH.
*
* @note This function should be called with the subscription list mutex locked.
*/
void _AwsIotJobs_DecrementReferences( _jobsOperation_t * pOperation,
char * pTopicBuffer,
_jobsSubscription_t ** pRemovedSubscription );
/*------------------------ Jobs serializer functions ------------------------*/
/**
* @brief Generates a Jobs JSON request document from an #AwsIotJobsRequestInfo_t
* and an #AwsIotJobsUpdateInfo_t.
*
* @param[in] type The type of Jobs operation for the request.
* @param[in] pRequestInfo Common Jobs request parameters.
* @param[in] pRequestContents Additional values to place in the JSON document,
* depending on `type`.
* @param[in] pOperation Operation associated with the Jobs request.
*
* @return #AWS_IOT_JOBS_SUCCESS on success; otherwise, #AWS_IOT_JOBS_NO_MEMORY.
*/
AwsIotJobsError_t _AwsIotJobs_GenerateJsonRequest( _jobsOperationType_t type,
const AwsIotJobsRequestInfo_t * pRequestInfo,
const _jsonRequestContents_t * pRequestContents,
_jobsOperation_t * pOperation );
/**
* @brief Parse a response received from the Jobs service.
*
* @param[in] status Either ACCEPTED or REJECTED.
* @param[in] pResponse The response received from the Jobs service.
* @param[in] responseLength Length of `pResponse`.
* @param[out] pOperation Associated Jobs operation, where parse results are
* written.
*/
void _AwsIotJobs_ParseResponse( AwsIotStatus_t status,
const char * pResponse,
size_t responseLength,
_jobsOperation_t * pOperation );
#endif /* ifndef AWS_IOT_JOBS_INTERNAL_H_ */

View file

@ -0,0 +1,5 @@
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,11
[InternetShortcut]
IDList=
URL=https://www.freertos.org/shadow

View file

@ -0,0 +1,909 @@
/*
* AWS IoT Shadow V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_shadow.h
* @brief User-facing functions of the Shadow library.
*/
#ifndef AWS_IOT_SHADOW_H_
#define AWS_IOT_SHADOW_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Shadow types include. */
#include "types/aws_iot_shadow_types.h"
/*------------------------ Shadow library functions -------------------------*/
/**
* @functionspage{shadow,Shadow library}
* - @functionname{shadow_function_init}
* - @functionname{shadow_function_cleanup}
* - @functionname{shadow_function_deleteasync}
* - @functionname{shadow_function_deletesync}
* - @functionname{shadow_function_getasync}
* - @functionname{shadow_function_getsync}
* - @functionname{shadow_function_updateasync}
* - @functionname{shadow_function_updatesync}
* - @functionname{shadow_function_wait}
* - @functionname{shadow_function_setdeltacallback}
* - @functionname{shadow_function_setupdatedcallback}
* - @functionname{shadow_function_removepersistentsubscriptions}
* - @functionname{shadow_function_strerror}
*/
/**
* @functionpage{AwsIotShadow_Init,shadow,init}
* @functionpage{AwsIotShadow_Cleanup,shadow,cleanup}
* @functionpage{AwsIotShadow_DeleteAsync,shadow,deleteasync}
* @functionpage{AwsIotShadow_DeleteSync,shadow,deletesync}
* @functionpage{AwsIotShadow_GetAsync,shadow,getasync}
* @functionpage{AwsIotShadow_GetSync,shadow,getsync}
* @functionpage{AwsIotShadow_UpdateAsync,shadow,updateasync}
* @functionpage{AwsIotShadow_UpdateSync,shadow,updatesync}
* @functionpage{AwsIotShadow_Wait,shadow,wait}
* @functionpage{AwsIotShadow_SetDeltaCallback,shadow,setdeltacallback}
* @functionpage{AwsIotShadow_SetUpdatedCallback,shadow,setupdatedcallback}
* @functionpage{AwsIotShadow_RemovePersistentSubscriptions,shadow,removepersistentsubscriptions}
* @functionpage{AwsIotShadow_strerror,shadow,strerror}
*/
/**
* @brief One-time initialization function for the Shadow library.
*
* This function performs internal setup of the Shadow library. <b>It must be
* called once (and only once) before calling any other Shadow function.</b>
* Calling this function more than once without first calling @ref
* shadow_function_cleanup may result in a crash.
*
* @param[in] mqttTimeout The amount of time (in milliseconds) that the Shadow
* library will wait for MQTT operations. Optional; set this to `0` to use
* @ref AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS.
*
* @return One of the following:
* - #AWS_IOT_SHADOW_SUCCESS
* - #AWS_IOT_SHADOW_INIT_FAILED
*
* @warning No thread-safety guarantees are provided for this function.
*
* @see @ref shadow_function_cleanup
*/
/* @[declare_shadow_init] */
AwsIotShadowError_t AwsIotShadow_Init( uint32_t mqttTimeout );
/* @[declare_shadow_init] */
/**
* @brief One-time deinitialization function for the Shadow library.
*
* This function frees resources taken in @ref shadow_function_init and deletes
* any [persistent subscriptions.](@ref AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS)
* It should be called to clean up the Shadow library. After this function returns,
* @ref shadow_function_init must be called again before calling any other Shadow
* function.
*
* @warning No thread-safety guarantees are provided for this function.
*
* @see @ref shadow_function_init
*/
/* @[declare_shadow_cleanup] */
void AwsIotShadow_Cleanup( void );
/* @[declare_shadow_cleanup] */
/**
* @brief Delete a Thing Shadow and receive an asynchronous notification when
* the Delete completes.
*
* This function deletes any existing Shadow document for the given Thing Name.
* If the given Thing has no Shadow and this function is called, the result will
* be #AWS_IOT_SHADOW_NOT_FOUND.
*
* Deleting a Shadow involves sending an MQTT message to AWS IoT and waiting on
* a response. This message will always be sent at [MQTT QoS 0](@ref #IOT_MQTT_QOS_0).
*
* @param[in] mqttConnection The MQTT connection to use for Shadow delete.
* @param[in] pThingName The Thing Name associated with the Shadow to delete.
* @param[in] thingNameLength The length of `pThingName`.
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pDeleteOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Shadow delete
* completes.
*
* @return This function will return #AWS_IOT_SHADOW_STATUS_PENDING upon successfully
* queuing a Shadow delete.
* @return If this function fails before queuing a Shadow delete, it will return one of:
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
* - #AWS_IOT_SHADOW_BAD_PARAMETER
* - #AWS_IOT_SHADOW_NO_MEMORY
* @return Upon successful completion of the Shadow delete (either through an #AwsIotShadowCallbackInfo_t
* or #AwsIotShadow_Wait), the status will be #AWS_IOT_SHADOW_SUCCESS.
* @return Should the Shadow delete fail, the status will be one of:
* - #AWS_IOT_SHADOW_MQTT_ERROR
* - #AWS_IOT_SHADOW_BAD_RESPONSE
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
*
* @see @ref shadow_function_deletesync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* // Shadow operation handle.
* AwsIotShadowOperation_t deleteOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
*
* // Queue a Shadow delete.
* AwsIotShadowError_t deleteResult = AwsIotShadow_DeleteAsync( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* AWS_IOT_SHADOW_FLAG_WAITABLE,
* NULL,
* &deleteOperation );
*
* // Shadow delete should return AWS_IOT_SHADOW_STATUS_PENDING upon success.
* if( deleteResult == AWS_IOT_SHADOW_STATUS_PENDING )
* {
* // Wait for the Shadow delete to complete.
* deleteResult = AwsIotShadow_Wait( deleteOperation, 5000 );
*
* // Delete result should be AWS_IOT_SHADOW_SUCCESS upon successfully
* // deleting an existing Shadow.
* }
* @endcode
*/
/* @[declare_shadow_deleteasync] */
AwsIotShadowError_t AwsIotShadow_DeleteAsync( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
uint32_t flags,
const AwsIotShadowCallbackInfo_t * pCallbackInfo,
AwsIotShadowOperation_t * const pDeleteOperation );
/* @[declare_shadow_deleteasync] */
/**
* @brief Delete a Thing Shadow with a timeout.
*
* This function queues a Shadow delete, then waits for the result. Internally, this
* function is a call to @ref shadow_function_deleteasync followed by @ref shadow_function_wait.
* See @ref shadow_function_deleteasync for more information on the Shadow delete operation.
*
* @param[in] mqttConnection The MQTT connection to use for Shadow delete.
* @param[in] pThingName The Thing Name associated with the Shadow to delete.
* @param[in] thingNameLength The length of `pThingName`.
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
* @param[in] timeoutMs If the Shadow service does not respond to the Shadow delete
* within this timeout, this function returns #AWS_IOT_SHADOW_TIMEOUT.
*
* @return One of the following:
* - #AWS_IOT_SHADOW_SUCCESS
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
* - #AWS_IOT_SHADOW_BAD_PARAMETER
* - #AWS_IOT_SHADOW_NO_MEMORY
* - #AWS_IOT_SHADOW_MQTT_ERROR
* - #AWS_IOT_SHADOW_BAD_RESPONSE
* - #AWS_IOT_SHADOW_TIMEOUT
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
*/
/* @[declare_shadow_deletesync] */
AwsIotShadowError_t AwsIotShadow_DeleteSync( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
uint32_t flags,
uint32_t timeoutMs );
/* @[declare_shadow_deletesync] */
/**
* @brief Retrieve a Thing Shadow and receive an asynchronous notification when
* the Shadow document is received.
*
* This function retrieves the Thing Shadow document currently stored by the
* Shadow service. If a given Thing has no Shadow and this function is called,
* the result will be #AWS_IOT_SHADOW_NOT_FOUND.
*
* Shadow documents may be large, and their size is not known beforehand.
* Therefore, this function works best when memory is dynamically allocated.
* Because the Shadow document is retrieved in an MQTT PUBLISH packet, the MQTT
* library will allocate a buffer for the Shadow document using #IotMqtt_MallocMessage.
*
* The MQTT library may free the buffer for a retrieved Shadow document as soon
* as the [Shadow completion callback](@ref AwsIotShadowCallbackInfo_t) returns.
* Therefore, any data needed later must be copied from the Shadow document.
* Similarly, if the flag #AWS_IOT_SHADOW_FLAG_WAITABLE is given to this function
* (which indicates that the Shadow document will be needed after the Shadow
* operation completes), #AwsIotShadowDocumentInfo_t.mallocDocument must be
* provided to allocate a longer-lasting buffer.
*
* @note Because of the potentially large size of complete Shadow documents, it is more
* memory-efficient for most applications to use [delta callbacks]
* (@ref shadow_function_setdeltacallback) to retrieve Shadows from
* the Shadow service.
*
* @param[in] mqttConnection The MQTT connection to use for Shadow get.
* @param[in] pGetInfo Shadow document parameters.
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pGetOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Shadow get
* completes.
*
* @return This function will return #AWS_IOT_SHADOW_STATUS_PENDING upon successfully
* queuing a Shadow get.
* @return If this function fails before queuing a Shadow get, it will return one of:
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
* - #AWS_IOT_SHADOW_BAD_PARAMETER
* - #AWS_IOT_SHADOW_NO_MEMORY
* @return Upon successful completion of the Shadow get (either through an #AwsIotShadowCallbackInfo_t
* or #AwsIotShadow_Wait), the status will be #AWS_IOT_SHADOW_SUCCESS.
* @return Should the Shadow get fail, the status will be one of:
* - #AWS_IOT_SHADOW_NO_MEMORY (Memory could not be allocated for incoming document)
* - #AWS_IOT_SHADOW_MQTT_ERROR
* - #AWS_IOT_SHADOW_BAD_RESPONSE
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
*
* @see @ref shadow_function_getsync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* // Shadow get completion callback. The retrieved document will be in
* // pCallbackParam. Any data in the retrieved document needed after this
* // function returns must be copied.
* void _processRetrievedDocument( void * pCallbackContext,
* AwsIotShadowCallbackParam_t * pCallbackParam );
*
* // Parameters and return value of Shadow get.
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
* AwsIotShadowDocumentInfo_t getInfo = { ... };
* uint32_t timeout = 5000; // 5 seconds
*
* // Callback for get completion.
* AwsIotShadowCallbackInfo_t getCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
* getCallback.function = _processRetrievedDocument;
*
* // Shadow get operation.
* result = AwsIotShadow_GetAsync( mqttConnection,
* &getInfo,
* 0,
* &getCallback,
* NULL );
*
* // Get should have returned AWS_IOT_SHADOW_STATUS_PENDING. The function
* // _processRetrievedDocument will be invoked once the Shadow get completes.
* @endcode
*
* See @ref shadow_function_wait <b>Example 2</b> for an example of using this
* function with #AWS_IOT_SHADOW_FLAG_WAITABLE and @ref shadow_function_wait.
*/
/* @[declare_shadow_getasync] */
AwsIotShadowError_t AwsIotShadow_GetAsync( IotMqttConnection_t mqttConnection,
const AwsIotShadowDocumentInfo_t * pGetInfo,
uint32_t flags,
const AwsIotShadowCallbackInfo_t * pCallbackInfo,
AwsIotShadowOperation_t * const pGetOperation );
/* @[declare_shadow_getasync] */
/**
* @brief Retrieve a Thing Shadow with a timeout.
*
* This function queues a Shadow get, then waits for the result. Internally, this
* function is a call to @ref shadow_function_getasync followed by @ref shadow_function_wait.
* See @ref shadow_function_getasync for more information on the Shadow get operation.
*
* @param[in] mqttConnection The MQTT connection to use for Shadow get.
* @param[in] pGetInfo Shadow document parameters.
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
* @param[in] timeoutMs If the Shadow service does not respond to the Shadow get
* within this timeout, this function returns #AWS_IOT_SHADOW_TIMEOUT.
* @param[out] pShadowDocument A pointer to a buffer containing the Shadow document
* retrieved by a Shadow get is placed here. The buffer was allocated with the function
* `pGetInfo->get.mallocDocument`. This output parameter is only valid if this function
* returns #AWS_IOT_SHADOW_SUCCESS.
* @param[out] pShadowDocumentLength The length of the Shadow document in
* `pShadowDocument` is placed here. This output parameter is only valid if this function
* returns #AWS_IOT_SHADOW_SUCCESS.
*
* @return One of the following:
* - #AWS_IOT_SHADOW_SUCCESS
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
* - #AWS_IOT_SHADOW_BAD_PARAMETER
* - #AWS_IOT_SHADOW_NO_MEMORY
* - #AWS_IOT_SHADOW_MQTT_ERROR
* - #AWS_IOT_SHADOW_BAD_RESPONSE
* - #AWS_IOT_SHADOW_TIMEOUT
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
*/
/* @[declare_shadow_getsync] */
AwsIotShadowError_t AwsIotShadow_GetSync( IotMqttConnection_t mqttConnection,
const AwsIotShadowDocumentInfo_t * pGetInfo,
uint32_t flags,
uint32_t timeoutMs,
const char ** const pShadowDocument,
size_t * const pShadowDocumentLength );
/* @[declare_shadow_getsync] */
/**
* @brief Send a Thing Shadow update and receive an asynchronous notification when
* the Shadow Update completes.
*
* This function modifies the Thing Shadow document stored by the Shadow service.
* If a given Thing has no Shadow and this function is called, then a new Shadow
* is created.
*
* New JSON keys in the Shadow document will be appended. For example, if the Shadow service
* currently has a document containing key `example1` and this function sends a document
* only containing key `example2`, then the resulting document in the Shadow service
* will contain both `example1` and `example2`.
*
* Existing JSON keys in the Shadow document will be replaced. For example, if the Shadow
* service currently has a document containing `"example1": [0,1,2]` and this function sends
* a document containing key `"example1": [1,2,3]`, then the resulting document in the Shadow
* service will contain `"example1": [1,2,3]`.
*
* Successful Shadow updates will trigger the [Shadow updated callback]
* (@ref shadow_function_setupdatedcallback). If the resulting Shadow document contains
* different `desired` and `reported` keys, then the [Shadow delta callback]
* (@ref shadow_function_setdeltacallback) will be triggered as well.
*
* @attention All documents passed to this function must contain a `clientToken`.
* The [client token]
* (https://docs.aws.amazon.com/iot/latest/developerguide/device-shadow-document.html#client-token)
* is a string used to distinguish between Shadow updates. They are limited to 64
* characters; attempting to use a client token longer than 64 characters will
* cause the Shadow update to fail. They must be unique at any given time, i.e.
* they may be reused <i>as long as no two Shadow updates are using the same
* client token at the same time</i>.
*
* @param[in] mqttConnection The MQTT connection to use for Shadow update.
* @param[in] pUpdateInfo Shadow document parameters.
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion.
* @param[out] pUpdateOperation Set to a handle by which this operation may be referenced
* after this function returns. This reference is invalidated once the Shadow update
* completes.
*
* @return This function will return #AWS_IOT_SHADOW_STATUS_PENDING upon successfully
* queuing a Shadow update.
* @return If this function fails before queuing a Shadow update, it will return one of:
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
* - #AWS_IOT_SHADOW_BAD_PARAMETER
* - #AWS_IOT_SHADOW_NO_MEMORY
* @return Upon successful completion of the Shadow update (either through an #AwsIotShadowCallbackInfo_t
* or #AwsIotShadow_Wait), the status will be #AWS_IOT_SHADOW_SUCCESS.
* @return Should the Shadow update fail, the status will be one of:
* - #AWS_IOT_SHADOW_MQTT_ERROR
* - #AWS_IOT_SHADOW_BAD_RESPONSE
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
*
* @see @ref shadow_function_updatesync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* // Shadow update completion callback.
* void _updateComplete( void * pCallbackContext,
* AwsIotShadowCallbackParam_t * pCallbackParam );
*
* // Parameters and return value of Shadow update.
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
* AwsIotShadowDocumentInfo_t updateInfo = { ... };
* uint32_t timeout = 5000; // 5 seconds
*
* // Set Shadow document to send.
* updateInfo.update.pUpdateDocument = "{...}"; // Must contain clientToken
* updateInfo.update.updateDocumentLength = strlen( updateInfo.update.pUpdateDocument );
*
* // Callback for update completion.
* AwsIotShadowCallbackInfo_t updateCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
* updateCallback.function = _updateComplete;
*
* // Shadow update operation.
* result = AwsIotShadow_UpdateAsync( mqttConnection,
* &updateInfo,
* 0,
* &updateCallback,
* NULL );
*
* // Update should have returned AWS_IOT_SHADOW_STATUS_PENDING. The function
* // _updateComplete will be invoked once the Shadow update completes.
* @endcode
*
* See @ref shadow_function_wait <b>Example 1</b> for an example of using this
* function with #AWS_IOT_SHADOW_FLAG_WAITABLE and @ref shadow_function_wait.
*/
/* @[declare_shadow_updateasync] */
AwsIotShadowError_t AwsIotShadow_UpdateAsync( IotMqttConnection_t mqttConnection,
const AwsIotShadowDocumentInfo_t * pUpdateInfo,
uint32_t flags,
const AwsIotShadowCallbackInfo_t * pCallbackInfo,
AwsIotShadowOperation_t * const pUpdateOperation );
/* @[declare_shadow_updateasync] */
/**
* @brief Send a Thing Shadow update with a timeout.
*
* This function queues a Shadow update, then waits for the result. Internally, this
* function is a call to @ref shadow_function_updateasync followed by @ref shadow_function_wait.
* See @ref shadow_function_updateasync for more information on the Shadow update operation.
*
* @param[in] mqttConnection The MQTT connection to use for Shadow update.
* @param[in] pUpdateInfo Shadow document parameters.
* @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
* @param[in] timeoutMs If the Shadow service does not respond to the Shadow update
* within this timeout, this function returns #AWS_IOT_SHADOW_TIMEOUT.
*
* @return One of the following:
* - #AWS_IOT_SHADOW_SUCCESS
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
* - #AWS_IOT_SHADOW_BAD_PARAMETER
* - #AWS_IOT_SHADOW_NO_MEMORY
* - #AWS_IOT_SHADOW_MQTT_ERROR
* - #AWS_IOT_SHADOW_BAD_RESPONSE
* - #AWS_IOT_SHADOW_TIMEOUT
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
*/
/* @[declare_shadow_updatesync] */
AwsIotShadowError_t AwsIotShadow_UpdateSync( IotMqttConnection_t mqttConnection,
const AwsIotShadowDocumentInfo_t * pUpdateInfo,
uint32_t flags,
uint32_t timeoutMs );
/* @[declare_shadow_updatesync] */
/**
* @brief Wait for a Shadow operation to complete.
*
* This function blocks to wait for a [delete](@ref shadow_function_deleteasync),
* [get](@ref shadow_function_getasync), or [update](@ref shadow_function_updateasync) to
* complete. These operations are by default asynchronous; the function calls
* queue an operation for processing, and a callback is invoked once the operation
* is complete.
*
* To use this function, the flag #AWS_IOT_SHADOW_FLAG_WAITABLE must have been
* set in the operation's function call. Additionally, this function must always
* be called with any waitable operation to clean up resources.
*
* Regardless of its return value, this function always clean up resources used
* by the waitable operation. This means `operation` is invalidated as soon as
* this function returns, even if it returns #AWS_IOT_SHADOW_TIMEOUT or another
* error.
*
* @param[in] operation Reference to the Shadow operation to wait for. The flag
* #AWS_IOT_SHADOW_FLAG_WAITABLE must have been set for this operation.
* @param[in] timeoutMs How long to wait before returning #AWS_IOT_SHADOW_TIMEOUT.
* @param[out] pShadowDocument A pointer to a buffer containing the Shadow document
* retrieved by a [Shadow get](@ref shadow_function_getasync) is placed here. The buffer
* was allocated with the function #AwsIotShadowDocumentInfo_t.mallocDocument passed
* to @ref shadow_function_getasync. This parameter is only valid for a [Shadow get]
* (@ref shadow_function_getasync) and ignored for other Shadow operations. This output
* parameter is only valid if this function returns #AWS_IOT_SHADOW_SUCCESS.
* @param[out] pShadowDocumentLength The length of the Shadow document in
* `pShadowDocument` is placed here. This parameter is only valid for a [Shadow get]
* (@ref shadow_function_getasync) and ignored for other Shadow operations. This output
* parameter is only valid if this function returns #AWS_IOT_SHADOW_SUCCESS.
*
* @return One of the following:
* - #AWS_IOT_SHADOW_SUCCESS
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
* - #AWS_IOT_SHADOW_BAD_PARAMETER
* - #AWS_IOT_SHADOW_BAD_RESPONSE
* - #AWS_IOT_SHADOW_TIMEOUT
* - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
* and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
*
* <b>Example 1 (Shadow Update)</b>
* @code{c}
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
* AwsIotShadowDocumentInfo_t updateInfo = { ... };
*
* // Reference and timeout.
* AwsIotShadowOperation_t updateOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
* uint32_t timeout = 5000; // 5 seconds
*
* // Shadow update operation.
* result = AwsIotShadow_UpdateAsync( mqttConnection,
* &updateInfo,
* AWS_IOT_SHADOW_FLAG_WAITABLE,
* NULL,
* &updateOperation );
*
* // Update should have returned AWS_IOT_SHADOW_STATUS_PENDING. The call to wait
* // returns once the result of the update is available or the timeout expires.
* if( result == AWS_IOT_SHADOW_STATUS_PENDING )
* {
* // The last two parameters are ignored for a Shadow update.
* result = AwsIotShadow_Wait( updateOperation, timeout, NULL, NULL );
*
* // After the call to wait, the result of the update is known
* // (not AWS_IOT_SHADOW_STATUS_PENDING).
* assert( result != AWS_IOT_SHADOW_STATUS_PENDING );
* }
* @endcode
*
* <b>Example 2 (Shadow Get)</b>
* @code{c}
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
* AwsIotShadowDocumentInfo_t getInfo = { ... };
*
* // Reference and timeout.
* AwsIotShadowOperation_t getOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
* uint32_t timeout = 5000; // 5 seconds
*
* // Buffer pointer and size for retrieved Shadow document.
* const char * pShadowDocument = NULL;
* size_t documentLength = 0;
*
* // Buffer allocation function must be set for a waitable Shadow get.
* getInfo.get.mallocDocument = malloc;
*
* // Shadow get operation.
* result = AwsIotShadow_GetAsync( mqttConnection,
* &getInfo,
* AWS_IOT_SHADOW_FLAG_WAITABLE,
* NULL,
* &getOperation );
*
* // Get should have returned AWS_IOT_SHADOW_STATUS_PENDING. The call to wait
* // returns once the result of the get is available or the timeout expires.
* if( result == AWS_IOT_SHADOW_STATUS_PENDING )
* {
* // The last two parameters must be set for a Shadow get.
* result = AwsIotShadow_Wait( getOperation, timeout, &pShadowDocument, &documentLength );
*
* // After the call to wait, the result of the get is known
* // (not AWS_IOT_SHADOW_STATUS_PENDING).
* assert( result != AWS_IOT_SHADOW_STATUS_PENDING );
*
* // The retrieved Shadow document is only valid for a successful Shadow get.
* if( result == AWS_IOT_SHADOW_SUCCESS )
* {
* // Do something with the Shadow document...
*
* // Free the Shadow document when finished.
* free( pShadowDocument );
* }
* }
* @endcode
*/
/* @[declare_shadow_wait] */
AwsIotShadowError_t AwsIotShadow_Wait( AwsIotShadowOperation_t operation,
uint32_t timeoutMs,
const char ** const pShadowDocument,
size_t * const pShadowDocumentLength );
/* @[declare_shadow_wait] */
/**
* @brief Set a callback to be invoked when the Thing Shadow `desired` and `reported`
* states differ.
*
* A Thing Shadow contains `reported` and `desired` states, meant to represent
* the current device status and some desired status, respectively. When the
* `reported` and `desired` states differ, the Thing Shadow service generates a
* <i>delta document</i> and publishes it to the topic `update/delta`. Devices
* with a subscription for this topic will receive the delta document and may act
* based on the different `reported` and `desired` states. See [this page]
* (https://docs.aws.amazon.com/iot/latest/developerguide/using-device-shadows.html#delta-state)
* for more information about using delta documents.
*
* A <i>delta callback</i> may be invoked whenever a delta document is generated.
* Each Thing may have a single delta callback set. This function modifies the delta
* callback for a specific Thing depending on the `pDeltaCallback` parameter and
* the presence of any existing delta callback:
* - When no existing delta callback exists for a specific Thing, a new delta
* callback is added.
* - If there is an existing delta callback and `pDeltaCallback` is not `NULL`, then
* the existing callback function and parameter are replaced with `pDeltaCallback`.
* - If there is an existing subscription and `pDeltaCallback` is `NULL`, then the
* delta callback is removed.
*
* This function is always blocking; it may block for up to the default MQTT
* timeout. This timeout is set as a parameter to @ref shadow_function_init,
* and defaults to @ref AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS if not set. If
* this function's underlying MQTT operations fail to complete within this
* timeout, then this function returns #AWS_IOT_SHADOW_TIMEOUT.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription to
* `update/delta`.
* @param[in] pThingName The subscription to `update/delta` will be added for
* this Thing Name.
* @param[in] thingNameLength The length of `pThingName`.
* @param[in] flags This parameter is for future-compatibility. Currently, flags
* are not supported for this function and this parameter is ignored.
* @param[in] pDeltaCallback Callback function to invoke for incoming delta
* documents.
*
* @return One of the following:
* - #AWS_IOT_SHADOW_SUCCESS
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
* - #AWS_IOT_SHADOW_BAD_PARAMETER
* - #AWS_IOT_SHADOW_NO_MEMORY
* - #AWS_IOT_SHADOW_MQTT_ERROR
* - #AWS_IOT_SHADOW_TIMEOUT
*
* @return This function always returns #AWS_IOT_SHADOW_SUCCESS when replacing or
* removing existing delta callbacks.
*
* @see @ref shadow_function_setupdatedcallback for the function to register
* callbacks for all Shadow updates.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
* AwsIotShadowCallbackInfo_t deltaCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
*
* // _deltaCallbackFunction will be invoked when a delta document is received.
* deltaCallback.function = _deltaCallbackFunction;
*
* // Set the delta callback for the Thing "Test_device".
* result = AwsIotShadow_SetDeltaCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* &deltaCallback );
*
* // Check if callback was successfully set.
* if( result == AWS_IOT_SHADOW_SUCCESS )
* {
* AwsIotShadowDocumentInfo_t updateInfo = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
*
* // Set the Thing Name for Shadow update.
* updateInfo.pThingName = THING_NAME;
* updateInfo.thingNameLength = THING_NAME_LENGTH;
*
* // Set the Shadow document to send. This document has different "reported"
* // and "desired" states. It represents a scenario where a device is currently
* // off, but is being ordered to turn on.
* updateInfo.update.pUpdateDocument =
* "{"
* "\"state\": {"
* "\"reported\": { \"deviceOn\": false },"
* "\"desired\": { \"deviceOn\": true }"
* "}"
* "}";
* updateInfo.update.updateDocumentLength = strlen( updateInfo.update.pUpdateDocument );
*
* // Send the Shadow document with different "reported" and desired states.
* result = AwsIotShadow_UpdateSync( mqttConnection,
* &updateInfo,
* 0,
* 0,
* 5000 );
*
* // After the update is successfully sent, the function _deltaCallbackFunction
* // will be invoked once the Shadow service generates and sends a delta document.
* // The delta document will contain the different "deviceOn" states, as well as
* // metadata.
*
* // Once the delta callback is no longer needed, it may be removed by passing
* // NULL as pDeltaCallback.
* result = AwsIotShadow_SetDeltaCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* NULL );
*
* // The return value from removing a delta callback should always be success.
* assert( result == AWS_IOT_SHADOW_SUCCESS );
* }
* @endcode
*/
/* @[declare_shadow_setdeltacallback] */
AwsIotShadowError_t AwsIotShadow_SetDeltaCallback( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
uint32_t flags,
const AwsIotShadowCallbackInfo_t * pDeltaCallback );
/* @[declare_shadow_setdeltacallback] */
/**
* @brief Set a callback to be invoked when a Thing Shadow changes.
*
* The Shadow service publishes a state document to the `update/documents` topic
* whenever a Thing Shadow is successfully updated. This document reports the
* complete previous and current Shadow documents in `previous` and `current`
* sections, respectively. Therefore, the `update/documents` topic is useful
* for monitoring Shadow updates.
*
* An <i>updated callback</i> may be invoked whenever a document is published to
* `update/documents`. Each Thing may have a single updated callback set. This function
* modifies the updated callback for a specific Thing depending on the `pUpdatedCallback`
* parameter and the presence of any existing updated callback.
* - When no existing updated callback exists for a specific Thing, a new updated
* callback is added.
* - If there is an existing updated callback and `pUpdatedCallback` is not `NULL`,
* then the existing callback function and parameter are replaced with `pUpdatedCallback`.
* - If there is an existing updated callback and `pUpdatedCallback` is `NULL`,
* then the updated callback is removed.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription to `update/documents`.
* @param[in] pThingName The subscription to `update/documents` will be added for
* this Thing Name.
* @param[in] thingNameLength The length of `pThingName`.
* @param[in] flags This parameter is for future-compatibility. Currently, flags are
* not supported for this function and this parameter is ignored.
* @param[in] pUpdatedCallback Callback function to invoke for incoming updated documents.
*
* @return One of the following:
* - #AWS_IOT_SHADOW_SUCCESS
* - #AWS_IOT_SHADOW_NOT_INITIALIZED
* - #AWS_IOT_SHADOW_BAD_PARAMETER
* - #AWS_IOT_SHADOW_NO_MEMORY
* - #AWS_IOT_SHADOW_MQTT_ERROR
* - #AWS_IOT_SHADOW_TIMEOUT
*
* @note Documents published to `update/documents` will be large, as they contain 2
* complete Shadow state documents. If an updated callback is used, ensure that the
* device has sufficient memory for incoming documents.
*
* @see @ref shadow_function_setdeltacallback for the function to register callbacks
* for delta documents.
*
* <b>Example</b>
* @code{c}
* #define THING_NAME "Test_device"
* #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
*
* AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
* AwsIotShadowCallbackInfo_t updatedCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
*
* // _updatedCallbackFunction will be invoked when an updated document is received.
* updatedCallback.function = _updatedCallbackFunction;
*
* // Set the updated callback for the Thing "Test_device".
* result = AwsIotShadow_SetUpdatedCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* 0,
* &updatedCallback );
*
* // Check if the callback was successfully set.
* if( result == AWS_IOT_SHADOW_SUCCESS )
* {
* AwsIotShadowDocumentInfo_t updateInfo = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
*
* // Set the Thing Name for Shadow update.
* updateInfo.pThingName = THING_NAME;
* updateInfo.thingNameLength = THING_NAME_LENGTH;
*
* // Set the Shadow document to send. Any Shadow update will trigger the
* // updated callback.
* updateInfo.update.pUpdateDocument =
* "{"
* "\"state\": {"
* "\"reported\": { \"deviceOn\": false }"
* "}"
* "}";
* updateInfo.update.updateDocumentLength = strlen( updateInfo.update.pUpdateDocument );
*
* // Send the Shadow document. A successful update will trigger the updated callback.
* result = AwsIotShadow_UpdateSync( mqttConnection,
* &updateInfo,
* 0,
* 0,
* 5000 );
*
* // After a successful Shadow update, the updated callback will be invoked.
*
* // Once the updated callback is no longer needed, it may be removed by
* // passing NULL as pUpdatedCallback.
* result = AwsIotShadow_SetUpdatedCallback( mqttConnection,
* THING_NAME,
* THING_NAME_LENGTH,
* NULL );
*
* // The return value from removing an updated callback should always be
* // success.
* assert( result == AWS_IOT_SHADOW_SUCCESS );
* }
* @endcode
*/
/* @[declare_shadow_setupdatedcallback] */
AwsIotShadowError_t AwsIotShadow_SetUpdatedCallback( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
uint32_t flags,
const AwsIotShadowCallbackInfo_t * pUpdatedCallback );
/* @[declare_shadow_setupdatedcallback] */
/**
* @brief Remove persistent Thing Shadow operation topic subscriptions.
*
* Passing the flag @ref AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS to @ref shadow_function_deleteasync,
* @ref shadow_function_getasync, @ref shadow_function_updateasync, or their blocking versions.
* causes the Shadow operation topic subscriptions to be maintained for future calls to the
* same function. If a persistent subscription for a Shadow topic are no longer needed,
* this function may be used to remove it.
*
* @param[in] mqttConnection The MQTT connection associated with the persistent subscription.
* @param[in] pThingName The Thing Name associated with the persistent subscription.
* @param[in] thingNameLength The length of `pThingName`.
* @param[in] flags Flags that determine which subscriptions to remove. Valid values are
* the bitwise OR of the following individual flags:
* - @ref AWS_IOT_SHADOW_FLAG_REMOVE_DELETE_SUBSCRIPTIONS
* - @ref AWS_IOT_SHADOW_FLAG_REMOVE_GET_SUBSCRIPTIONS
* - @ref AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS
*
* @return On success:
* - #AWS_IOT_SHADOW_SUCCESS
* @return If an MQTT UNSUBSCRIBE packet cannot be sent, one of the following:
* - #AWS_IOT_SHADOW_NO_MEMORY
* - #AWS_IOT_SHADOW_MQTT_ERROR
*
* @note @ref shadow_function_cleanup removes all persistent subscriptions as well.
*
* @warning This function is not safe to call with any in-progress operations!
* It also does not affect delta and updated callbacks registered with @ref
* shadow_function_setdeltacallback and @ref shadow_function_setupdatedcallback,
* respectively. (See documentation for those functions on how to remove their
* callbacks).
*/
/* @[declare_shadow_removepersistentsubscriptions] */
AwsIotShadowError_t AwsIotShadow_RemovePersistentSubscriptions( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
uint32_t flags );
/* @[declare_shadow_removepersistentsubscriptions] */
/*------------------------- Shadow helper functions -------------------------*/
/**
* @brief Returns a string that describes an #AwsIotShadowError_t.
*
* Like POSIX's `strerror`, this function returns a string describing a return
* code. In this case, the return code is a Shadow library error code, `status`.
*
* The string returned by this function <b>MUST</b> be treated as read-only: any
* attempt to modify its contents may result in a crash. Therefore, this function
* is limited to usage in logging.
*
* @param[in] status The status to describe.
*
* @return A read-only string that describes `status`.
*
* @warning The string returned by this function must never be modified.
*/
/* @[declare_shadow_strerror] */
const char * AwsIotShadow_strerror( AwsIotShadowError_t status );
/* @[declare_shadow_strerror] */
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Backwards compatibility macros for previous function names.
*/
#define AwsIotShadow_Delete AwsIotShadow_DeleteAsync
#define AwsIotShadow_TimedDelete AwsIotShadow_DeleteSync
#define AwsIotShadow_Get AwsIotShadow_GetAsync
#define AwsIotShadow_TimedGet AwsIotShadow_GetSync
#define AwsIotShadow_Update AwsIotShadow_UpdateAsync
#define AwsIotShadow_TimedUpdate AwsIotShadow_UpdateSync
/** @endcond */
#endif /* ifndef AWS_IOT_SHADOW_H_ */

View file

@ -0,0 +1,641 @@
/*
* AWS IoT Shadow V2.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_shadow_types.h
* @brief Types of the Thing Shadow library.
*/
#ifndef AWS_IOT_SHADOW_TYPES_H_
#define AWS_IOT_SHADOW_TYPES_H_
/* The config header is always included first. */
#include "iot_config.h"
/* MQTT types include. */
#include "types/iot_mqtt_types.h"
/*--------------------------- Shadow handle types ---------------------------*/
/**
* @handles{shadow,Shadow library}
*/
/**
* @ingroup shadow_datatypes_handles
* @brief Opaque handle that references an in-progress Shadow operation.
*
* Set as an output parameter of @ref shadow_function_deleteasync, @ref shadow_function_getasync,
* and @ref shadow_function_updateasync. These functions send a message to the Shadow
* service requesting a Shadow operation; the result of this operation is unknown
* until the Shadow service sends a response. Therefore, this handle serves as a
* reference to Shadow operations awaiting a response from the Shadow service.
*
* This reference will be valid from the successful return of @ref shadow_function_deleteasync,
* @ref shadow_function_getasync, or @ref shadow_function_updateasync. The reference becomes
* invalid once the [completion callback](@ref AwsIotShadowCallbackInfo_t) is invoked,
* or @ref shadow_function_wait returns.
*
* @initializer{AwsIotShadowOperation_t,AWS_IOT_SHADOW_OPERATION_INITIALIZER}
*
* @see @ref shadow_function_wait and #AWS_IOT_SHADOW_FLAG_WAITABLE for waiting on
* a reference; or #AwsIotShadowCallbackInfo_t and #AwsIotShadowCallbackParam_t for an
* asynchronous notification of completion.
*/
typedef struct _shadowOperation * AwsIotShadowOperation_t;
/*------------------------- Shadow enumerated types -------------------------*/
/**
* @enums{shadow,Shadow library}
*/
/**
* @ingroup shadow_datatypes_enums
* @brief Return codes of [Shadow functions](@ref shadow_functions).
*
* The function @ref shadow_function_strerror can be used to get a return code's
* description.
*
* The values between 400 (#AWS_IOT_SHADOW_BAD_REQUEST) and 500
* (#AWS_IOT_SHADOW_SERVER_ERROR) may be returned by the Shadow service when it
* rejects a Shadow operation. See [this page]
* (https://docs.aws.amazon.com/iot/latest/developerguide/device-shadow-error-messages.html)
* for more information.
*/
typedef enum AwsIotShadowError
{
/**
* @brief Shadow operation completed successfully.
*
* Functions that may return this value:
* - @ref shadow_function_init
* - @ref shadow_function_wait
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_setdeltacallback
* - @ref shadow_function_setupdatedcallback
* - @ref shadow_function_removepersistentsubscriptions
*
* Will also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
* when successful.
*/
AWS_IOT_SHADOW_SUCCESS = 0,
/**
* @brief Shadow operation queued, awaiting result.
*
* Functions that may return this value:
* - @ref shadow_function_deleteasync
* - @ref shadow_function_getasync
* - @ref shadow_function_updateasync
*/
AWS_IOT_SHADOW_STATUS_PENDING = 1,
/**
* @brief Initialization failed.
*
* Functions that may return this value:
* - @ref shadow_function_init
*/
AWS_IOT_SHADOW_INIT_FAILED = 2,
/**
* @brief At least one parameter is invalid.
*
* Functions that may return this value:
* - @ref shadow_function_deleteasync and @ref shadow_function_deletesync
* - @ref shadow_function_getasync and @ref shadow_function_getsync
* - @ref shadow_function_updateasync and @ref shadow_function_updatesync
* - @ref shadow_function_wait
* - @ref shadow_function_setdeltacallback
* - @ref shadow_function_setupdatedcallback
*/
AWS_IOT_SHADOW_BAD_PARAMETER = 3,
/**
* @brief Shadow operation failed because of memory allocation failure.
*
* Functions that may return this value:
* - @ref shadow_function_deleteasync and @ref shadow_function_deletesync
* - @ref shadow_function_getasync and @ref shadow_function_getsync
* - @ref shadow_function_updateasync and @ref shadow_function_updatesync
* - @ref shadow_function_setdeltacallback
* - @ref shadow_function_setupdatedcallback
*/
AWS_IOT_SHADOW_NO_MEMORY = 4,
/**
* @brief Shadow operation failed because of failure in MQTT library.
*
* Check the Shadow library logs for the error code returned by the MQTT
* library.
*
* Functions that may return this value:
* - @ref shadow_function_deleteasync and @ref shadow_function_deletesync
* - @ref shadow_function_getasync and @ref shadow_function_getsync
* - @ref shadow_function_updateasync and @ref shadow_function_updatesync
* - @ref shadow_function_setdeltacallback
* - @ref shadow_function_setupdatedcallback
* - @ref shadow_function_removepersistentsubscriptions
*/
AWS_IOT_SHADOW_MQTT_ERROR = 5,
/**
* @brief Response received from Shadow service not understood.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
*
* May also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
*/
AWS_IOT_SHADOW_BAD_RESPONSE = 7,
/**
* @brief A blocking Shadow operation timed out.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
* - @ref shadow_function_setdeltacallback
* - @ref shadow_function_setupdatedcallback
*/
AWS_IOT_SHADOW_TIMEOUT = 8,
/**
* @brief An API function was called before @ref shadow_function_init.
*
* Functions that may return this value:
* - @ref shadow_function_deleteasync and @ref shadow_function_deletesync
* - @ref shadow_function_getasync and @ref shadow_function_getsync
* - @ref shadow_function_updateasync and @ref shadow_function_updatesync
* - @ref shadow_function_wait
* - @ref shadow_function_setdeltacallback
* - @ref shadow_function_setupdatedcallback
*/
AWS_IOT_SHADOW_NOT_INITIALIZED = 11,
/**
* @brief Shadow operation rejected: Bad request.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
*
* May also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
*/
AWS_IOT_SHADOW_BAD_REQUEST = 400,
/**
* @brief Shadow operation rejected: Unauthorized.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
*
* May also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
*/
AWS_IOT_SHADOW_UNAUTHORIZED = 401,
/**
* @brief Shadow operation rejected: Forbidden.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
*
* May also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
*/
AWS_IOT_SHADOW_FORBIDDEN = 403,
/**
* @brief Shadow operation rejected: Thing not found.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
*
* May also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
*/
AWS_IOT_SHADOW_NOT_FOUND = 404,
/**
* @brief Shadow operation rejected: Version conflict.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
*
* May also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
*/
AWS_IOT_SHADOW_CONFLICT = 409,
/**
* @brief Shadow operation rejected: The payload exceeds the maximum size
* allowed.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
*
* May also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
*/
AWS_IOT_SHADOW_TOO_LARGE = 413,
/**
* @brief Shadow operation rejected: Unsupported document encoding; supported
* encoding is UTF-8.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
*
* May also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
*/
AWS_IOT_SHADOW_UNSUPPORTED = 415,
/**
* @brief Shadow operation rejected: The Device Shadow service will generate
* this error message when there are more than 10 in-flight requests.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
*
* May also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
*/
AWS_IOT_SHADOW_TOO_MANY_REQUESTS = 429,
/**
* @brief Shadow operation rejected: Internal service failure.
*
* Functions that may return this value:
* - @ref shadow_function_deletesync
* - @ref shadow_function_getsync
* - @ref shadow_function_updatesync
* - @ref shadow_function_wait
*
* May also be the value of a Shadow operation completion callback's<br>
* [AwsIotShadowCallbackParam_t.operation.result](@ref AwsIotShadowCallbackParam_t.result)
*/
AWS_IOT_SHADOW_SERVER_ERROR = 500,
} AwsIotShadowError_t;
/**
* @ingroup shadow_datatypes_enums
* @brief Types of Shadow library callbacks.
*
* One of these values will be placed in #AwsIotShadowCallbackParam_t.callbackType
* to identify the reason for invoking a callback function.
*/
typedef enum AwsIotShadowCallbackType
{
AWS_IOT_SHADOW_DELETE_COMPLETE, /**< Callback invoked because a [Shadow delete](@ref shadow_function_deleteasync) completed. */
AWS_IOT_SHADOW_GET_COMPLETE, /**< Callback invoked because a [Shadow get](@ref shadow_function_getasync) completed. */
AWS_IOT_SHADOW_UPDATE_COMPLETE, /**< Callback invoked because a [Shadow update](@ref shadow_function_updateasync) completed. */
AWS_IOT_SHADOW_DELTA_CALLBACK, /**< Callback invoked for an incoming message on a [Shadow delta](@ref shadow_function_setdeltacallback) topic. */
AWS_IOT_SHADOW_UPDATED_CALLBACK /**< Callback invoked for an incoming message on a [Shadow updated](@ref shadow_function_setupdatedcallback) topic. */
} AwsIotShadowCallbackType_t;
/*------------------------- Shadow parameter structs ------------------------*/
/**
* @paramstructs{shadow,Shadow}
*/
/**
* @ingroup shadow_datatypes_paramstructs
* @brief Parameter to a Shadow callback function.
*
* @paramfor Shadow callback functions
*
* The Shadow library passes this struct to a callback function whenever a
* Shadow operation completes or a message is received on a Shadow delta or
* updated topic.
*
* The valid members of this struct are different based on
* #AwsIotShadowCallbackParam_t.callbackType. If the callback type is
* #AWS_IOT_SHADOW_DELETE_COMPLETE, #AWS_IOT_SHADOW_GET_COMPLETE, or
* #AWS_IOT_SHADOW_UPDATE_COMPLETE, then #AwsIotShadowCallbackParam_t.operation
* is valid. Otherwise, if the callback type is #AWS_IOT_SHADOW_DELTA_CALLBACK
* or #AWS_IOT_SHADOW_UPDATED_CALLBACK, then #AwsIotShadowCallbackParam_t.callback
* is valid.
*
* @attention Any pointers in this callback parameter may be freed as soon as the
* [callback function](@ref AwsIotShadowCallbackInfo_t.function) returns. Therefore,
* data must be copied if it is needed after the callback function returns.
* @attention The Shadow library may set strings that are not NULL-terminated.
*
* @see #AwsIotShadowCallbackInfo_t for the signature of a callback function.
*/
typedef struct AwsIotShadowCallbackParam
{
AwsIotShadowCallbackType_t callbackType; /**< @brief Reason for invoking the Shadow callback function to provide context. */
const char * pThingName; /**< @brief The Thing Name associated with this Shadow callback. */
size_t thingNameLength; /**< @brief Length of #AwsIotShadowCallbackParam_t.pThingName. */
IotMqttConnection_t mqttConnection; /**< @brief The MQTT connection associated with the Shadow callback. */
union
{
/* Valid for completed Shadow operations. */
struct
{
/* Valid for a completed Shadow GET operation. */
struct
{
const char * pDocument; /**< @brief Retrieved Shadow document. */
size_t documentLength; /**< @brief Length of retrieved Shadow document. */
} get; /**< @brief Retrieved Shadow document, valid only for a completed [Shadow Get](@ref shadow_function_getasync). */
AwsIotShadowError_t result; /**< @brief Result of Shadow operation, e.g. succeeded or failed. */
AwsIotShadowOperation_t reference; /**< @brief Reference to the Shadow operation that completed. */
} operation; /**< @brief Information on a completed Shadow operation. */
/* Valid for a message on a Shadow delta or updated topic. */
struct
{
const char * pDocument; /**< @brief Shadow delta or updated document. */
size_t documentLength; /**< @brief Length of Shadow delta or updated document. */
} callback; /**< @brief Shadow document from an incoming delta or updated topic. */
} u; /**< @brief Valid member depends on callback type. */
} AwsIotShadowCallbackParam_t;
/**
* @ingroup shadow_datatypes_paramstructs
* @brief Information on a user-provided Shadow callback function.
*
* @paramfor @ref shadow_function_deleteasync, @ref shadow_function_getasync, @ref
* shadow_function_updateasync, @ref shadow_function_setdeltacallback, @ref
* shadow_function_setupdatedcallback
*
* Provides a function to be invoked when a Shadow operation completes or when a
* Shadow document is received on a callback topic (delta or updated).
*
* @initializer{AwsIotShadowCallbackInfo_t,AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER}
*/
typedef struct AwsIotShadowCallbackInfo
{
void * pCallbackContext; /**< @brief The first parameter to pass to the callback function. */
/**
* @brief User-provided callback function signature.
*
* @param[in] pCallbackContext #AwsIotShadowCallbackInfo_t.pCallbackContext
* @param[in] pCallbackParam Details on the outcome of the Shadow
* operation or an incoming Shadow document.
*
* @see #AwsIotShadowCallbackParam_t for more information on the second parameter.
*/
void ( * function )( void * pCallbackContext,
AwsIotShadowCallbackParam_t * pCallbackParam );
} AwsIotShadowCallbackInfo_t;
/**
* @ingroup shadow_datatypes_paramstructs
* @brief Information on a Shadow document for @ref shadow_function_getasync or @ref
* shadow_function_updateasync.
*
* @paramfor @ref shadow_function_getasync, @ref shadow_function_updateasync
*
* The valid members of this struct are different based on whether this struct
* is passed to @ref shadow_function_getasync or @ref shadow_function_updateasync. When
* passed to @ref shadow_function_getasync, the `get` member is valid. When passed to
* @ref shadow_function_updateasync, the `update` member is valid. All other members
* must always be set.
*
* @initializer{AwsIotShadowDocumentInfo_t,AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER}
*/
typedef struct AwsIotShadowDocumentInfo
{
const char * pThingName; /**< @brief The Thing Name associated with this Shadow document. */
size_t thingNameLength; /**< @brief Length of #AwsIotShadowDocumentInfo_t.pThingName. */
IotMqttQos_t qos; /**< @brief QoS when sending a Shadow get or update message. See #IotMqttPublishInfo_t.qos. */
uint32_t retryLimit; /**< @brief Maximum number of retries for a Shadow get or update message. See #IotMqttPublishInfo_t.retryLimit. */
uint32_t retryMs; /**< @brief First retry time for a Shadow get or update message. See IotMqttPublishInfo_t.retryMs. */
union
{
/* Valid for Shadow get. */
struct
{
/**
* @brief Function to allocate memory for an incoming Shadow document.
*
* @param[in] documentLength Length of the document that needs to
* be allocated.
* This only needs to be set if #AWS_IOT_SHADOW_FLAG_WAITABLE is passed to
* @ref shadow_function_getasync.
*/
void *( *mallocDocument )( size_t documentLength );
} get; /**< @brief Valid members for @ref shadow_function_getasync. */
/* Valid for Shadow update. */
struct
{
const char * pUpdateDocument; /**< @brief The Shadow document to send in the update. */
size_t updateDocumentLength; /**< @brief Length of Shadow update document. */
} update; /**< @brief Valid members for @ref shadow_function_updateasync. */
} u; /**< @brief Valid member depends on operation type. */
} AwsIotShadowDocumentInfo_t;
/*------------------------ Shadow defined constants -------------------------*/
/**
* @constantspage{shadow,Shadow library}
*
* @section shadow_constants_initializers Shadow Initializers
* @brief Provides default values for the data types of the Shadow library.
*
* @snippet this define_shadow_initializers
*
* All user-facing data types of the Shadow library should be initialized
* using one of the following.
*
* @warning Failing to initialize a Shadow data type with the appropriate
* initializer may result in undefined behavior!
* @note The initializers may change at any time in future versions, but their
* names will remain the same.
*
* <b>Example</b>
* @code{c}
* AwsIotShadowCallbackInfo_t callbackInfo = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
* AwsIotShadowDocumentInfo_t documentInfo = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
* AwsIotShadowOperation_t operation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
* @endcode
*
* @section shadow_constants_flags Shadow Function Flags
* @brief Flags that modify the behavior of Shadow library functions.
*
* Flags should be bitwise-ORed with each other to change the behavior of
* Shadow library functions.
*
* The following flags are valid for the Shadow operation functions:
* @ref shadow_function_deleteasync, @ref shadow_function_getasync, @ref shadow_function_updateasync,
* and their blocking versions.
* - #AWS_IOT_SHADOW_FLAG_WAITABLE <br>
* @copybrief AWS_IOT_SHADOW_FLAG_WAITABLE
* - #AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS <br>
* @copybrief AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS
*
* The following flags are valid for @ref shadow_function_removepersistentsubscriptions.
* These flags are not valid for the Shadow operation functions.
* - #AWS_IOT_SHADOW_FLAG_REMOVE_DELETE_SUBSCRIPTIONS <br>
* @copybrief AWS_IOT_SHADOW_FLAG_REMOVE_DELETE_SUBSCRIPTIONS
* - #AWS_IOT_SHADOW_FLAG_REMOVE_GET_SUBSCRIPTIONS <br>
* @copybrief AWS_IOT_SHADOW_FLAG_REMOVE_GET_SUBSCRIPTIONS
* - #AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS <br>
* @copybrief AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS
*
* @note The values of the flags may change at any time in future versions, but
* their names will remain the same. Additionally, flags which may be used at
* the same time will be bitwise-exclusive of each other.
*/
/* @[define_shadow_initializers] */
#define AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER { 0 } /**< @brief Initializer for #AwsIotShadowCallbackInfo_t. */
#define AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER { 0 } /**< @brief Initializer for #AwsIotShadowDocumentInfo_t. */
#define AWS_IOT_SHADOW_OPERATION_INITIALIZER NULL /**< @brief Initializer for #AwsIotShadowOperation_t. */
/* @[define_shadow_initializers] */
/**
* @brief Allows the use of @ref shadow_function_wait for blocking until completion.
*
* This flag is only valid if passed to the functions @ref shadow_function_deleteasync,
* @ref shadow_function_getasync, or @ref shadow_function_updateasync.
*
* An #AwsIotShadowOperation_t <b>MUST</b> be provided if this flag is set.
* Additionally, an #AwsIotShadowCallbackInfo_t <b>MUST NOT</b> be provided.
*
* @note If this flag is set, @ref shadow_function_wait <b>MUST</b> be called to
* clean up resources.
*/
#define AWS_IOT_SHADOW_FLAG_WAITABLE ( 0x00000001 )
/**
* @brief Maintain the subscriptions for the Shadow operation topics, even after
* this function returns.
*
* This flag is only valid if passed to the functions @ref shadow_function_deleteasync,
* @ref shadow_function_getasync, @ref shadow_function_updateasync, or their blocking versions.
*
* The Shadow service reports results of Shadow operations by publishing
* messages to MQTT topics. By default, the functions @ref shadow_function_deleteasync,
* @ref shadow_function_getasync, and @ref shadow_function_updateasync subscribe to the
* necessary topics, wait for the Shadow service to publish the result of the
* Shadow operation, then unsubscribe from those topics. This workflow is suitable
* for infrequent Shadow operations, but is inefficient for frequent, periodic
* Shadow operations (where subscriptions for the Shadow operation topics would be
* constantly added and removed).
*
* This flag causes @ref shadow_function_deleteasync, @ref shadow_function_getasync, or
* @ref shadow_function_updateasync to maintain Shadow operation topic subscriptions,
* even after the function returns. These subscriptions may then be used by a
* future call to the same function.
*
* This flags only needs to be set once, after which subscriptions are maintained
* and reused for a specific Thing Name and Shadow function. The function @ref
* shadow_function_removepersistentsubscriptions may be used to remove
* subscriptions maintained by this flag.
*/
#define AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS ( 0x00000002 )
/**
* @brief Remove the persistent subscriptions from a Shadow delete operation.
*
* This flag is only valid if passed to the function @ref
* shadow_function_removepersistentsubscriptions.
*
* This flag may be passed to @ref shadow_function_removepersistentsubscriptions
* to remove any subscriptions for a specific Thing Name maintained by a previous
* call to @ref shadow_function_deleteasync or @ref shadow_function_deletesync.
*
* @warning Do not call @ref shadow_function_removepersistentsubscriptions with
* this flag for Thing Names with any in-progress Shadow delete operations.
*/
#define AWS_IOT_SHADOW_FLAG_REMOVE_DELETE_SUBSCRIPTIONS ( 0x00000001 )
/**
* @brief Remove the persistent subscriptions from a Shadow get operation.
*
* This flag is only valid if passed to the function @ref
* shadow_function_removepersistentsubscriptions.
*
* This flag may be passed to @ref shadow_function_removepersistentsubscriptions
* to remove any subscriptions for a specific Thing Name maintained by a previous
* call to @ref shadow_function_getasync or @ref shadow_function_getsync.
*
* @warning Do not call @ref shadow_function_removepersistentsubscriptions with
* this flag for Thing Names with any in-progress Shadow get operations.
*/
#define AWS_IOT_SHADOW_FLAG_REMOVE_GET_SUBSCRIPTIONS ( 0x00000002 )
/**
* @brief Remove the persistent subscriptions from a Shadow update operation.
*
* This flag is only valid if passed to the function @ref
* shadow_function_removepersistentsubscriptions.
*
* This flag may be passed to @ref shadow_function_removepersistentsubscriptions
* to remove any subscriptions for a specific Thing Name maintained by a previous
* call to @ref shadow_function_updateasync or @ref shadow_function_updatesync.
*
* @warning Do not call @ref shadow_function_removepersistentsubscriptions with
* this flag for Thing Names with any in-progress Shadow update operations.
*/
#define AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS ( 0x00000004 )
#endif /* ifndef AWS_IOT_SHADOW_TYPES_H_ */

View file

@ -0,0 +1,925 @@
/*
* AWS IoT Shadow V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_shadow_operation.c
* @brief Implements functions that process Shadow operations.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* Shadow internal include. */
#include "private/aws_iot_shadow_internal.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
/* MQTT include. */
#include "iot_mqtt.h"
/* Error handling include. */
#include "iot_error.h"
/*-----------------------------------------------------------*/
/**
* @brief First parameter to #_shadowOperation_match.
*/
typedef struct _operationMatchParams
{
_shadowOperationType_t type; /**< @brief DELETE, GET, or UPDATE. */
const char * pThingName; /**< @brief Thing Name of Shadow operation. */
size_t thingNameLength; /**< @brief Length of #_operationMatchParams_t.pThingName. */
const char * pDocument; /**< @brief Shadow UPDATE response document. */
size_t documentLength; /**< @brief Length of #_operationMatchParams_t.pDocument. */
} _operationMatchParams_t;
/*-----------------------------------------------------------*/
/**
* @brief Match a received Shadow response with a Shadow operation awaiting a
* response.
*
* @param[in] pOperationLink Pointer to the link member of the #_shadowOperation_t
* to check.
* @param[in] pMatch Pointer to an #_operationMatchParams_t.
*
* @return `true` if `pMatch` matches the received response; `false` otherwise.
*/
static bool _shadowOperation_match( const IotLink_t * pOperationLink,
void * pMatch );
/**
* @brief Common function for processing received Shadow responses.
*
* @param[in] type DELETE, GET, or UPDATE.
* @param[in] pMessage Received Shadow response (as an MQTT PUBLISH message).
*/
static void _commonOperationCallback( _shadowOperationType_t type,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Invoked when a Shadow response is received for Shadow DELETE.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Shadow response (as an MQTT PUBLISH message).
*/
static void _deleteCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Invoked when a Shadow response is received for a Shadow GET.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Shadow response (as an MQTT PUBLISH message).
*/
static void _getCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Process an incoming Shadow document received when a Shadow GET is
* accepted.
*
* @param[in] pOperation The GET operation associated with the incoming Shadow
* document.
* @param[in] pPublishInfo The received Shadow document (as an MQTT PUBLISH
* message).
*
* @return #AWS_IOT_SHADOW_SUCCESS or #AWS_IOT_SHADOW_NO_MEMORY. Memory allocation
* only happens for a waitable `pOperation`.
*/
static AwsIotShadowError_t _processAcceptedGet( _shadowOperation_t * pOperation,
const IotMqttPublishInfo_t * pPublishInfo );
/**
* @brief Invoked when a Shadow response is received for a Shadow UPDATE.
*
* @param[in] pArgument Ignored.
* @param[in] pMessage Received Shadow response (as an MQTT PUBLISH message).
*/
static void _updateCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage );
/**
* @brief Notify of a completed Shadow operation.
*
* @param[in] pOperation The operation which completed.
*
* Depending on the parameters passed to a user-facing Shadow function, the
* notification will cause @ref shadow_function_wait to return or invoke a
* user-provided callback.
*/
static void _notifyCompletion( _shadowOperation_t * pOperation );
/**
* @brief Get a Shadow subscription to use with a Shadow operation.
*
* This function may use an existing Shadow subscription, or it may allocate a
* new one.
*
* @param[in] pThingName Thing Name associated with operation.
* @param[in] thingNameLength Length of `pThingName`.
* @param[in] pTopicBuffer Contains the topic to use for subscribing.
* @param[in] operationTopicLength The length of the base topic in `pTopicBuffer`.
* @param[in] pOperation Shadow operation that needs a subscription.
* @param[out] pFreeTopicBuffer Whether the caller may free `pTopicBuffer`
* (which may be assigned to a subscription).
*
* @return #AWS_IOT_SHADOW_SUCCESS or #AWS_IOT_SHADOW_NO_MEMORY
*/
static AwsIotShadowError_t _findSubscription( const char * pThingName,
size_t thingNameLength,
char * pTopicBuffer,
uint16_t operationTopicLength,
_shadowOperation_t * pOperation,
bool * pFreeTopicBuffer );
/*-----------------------------------------------------------*/
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
/**
* @brief Printable names for each of the Shadow operations.
*/
const char * const _pAwsIotShadowOperationNames[] =
{
"DELETE",
"GET",
"UPDATE",
"SET DELTA",
"SET UPDATED"
};
#endif /* if LIBRARY_LOG_LEVEL > IOT_LOG_NONE */
/**
* @brief List of active Shadow operations awaiting a response from the Shadow
* service.
*/
IotListDouble_t _AwsIotShadowPendingOperations = { 0 };
/**
* @brief Protects #_AwsIotShadowPendingOperations from concurrent access.
*/
IotMutex_t _AwsIotShadowPendingOperationsMutex;
/*-----------------------------------------------------------*/
static bool _shadowOperation_match( const IotLink_t * pOperationLink,
void * pMatch )
{
/* Because this function is called from a container function, the given link
* must never be NULL. */
AwsIotShadow_Assert( pOperationLink != NULL );
_shadowOperation_t * pOperation = IotLink_Container( _shadowOperation_t,
pOperationLink,
link );
_operationMatchParams_t * pParam = ( _operationMatchParams_t * ) pMatch;
_shadowSubscription_t * pSubscription = pOperation->pSubscription;
const char * pClientToken = NULL;
size_t clientTokenLength = 0;
/* Check for matching Thing Name and operation type. */
bool match = ( pOperation->type == pParam->type ) &&
( pParam->thingNameLength == pSubscription->thingNameLength ) &&
( strncmp( pParam->pThingName,
pSubscription->pThingName,
pParam->thingNameLength ) == 0 );
/* For a Shadow UPDATE operation, compare the client tokens. */
if( ( match == true ) && ( pOperation->type == SHADOW_UPDATE ) )
{
/* Check document pointers. */
AwsIotShadow_Assert( pParam->pDocument != NULL );
AwsIotShadow_Assert( pParam->documentLength > 0 );
AwsIotShadow_Assert( pOperation->u.update.pClientToken != NULL );
AwsIotShadow_Assert( pOperation->u.update.clientTokenLength > 0 );
IotLogDebug( "Verifying client tokens for Shadow UPDATE." );
/* Check for the client token in the UPDATE response document. */
match = AwsIot_GetClientToken( pParam->pDocument,
pParam->documentLength,
&pClientToken,
&clientTokenLength );
/* If the UPDATE response document has a client token, check that it
* matches. */
if( match == true )
{
match = ( clientTokenLength == pOperation->u.update.clientTokenLength ) &&
( strncmp( pClientToken,
pOperation->u.update.pClientToken,
clientTokenLength ) == 0 );
}
else
{
IotLogWarn( "Received a Shadow UPDATE response with no client token. "
"This is possibly a response to a bad JSON document:\r\n%.*s",
pParam->documentLength,
pParam->pDocument );
}
}
return match;
}
/*-----------------------------------------------------------*/
static void _commonOperationCallback( _shadowOperationType_t type,
IotMqttCallbackParam_t * pMessage )
{
_shadowOperation_t * pOperation = NULL;
IotLink_t * pOperationLink = NULL;
AwsIotStatus_t status = AWS_IOT_UNKNOWN;
_operationMatchParams_t param = { .type = ( _shadowOperationType_t ) 0 };
uint32_t flags = 0;
/* Set operation type to search. */
param.type = type;
/* Set the response document for a Shadow UPDATE. */
if( type == SHADOW_UPDATE )
{
param.pDocument = pMessage->u.message.info.pPayload;
param.documentLength = pMessage->u.message.info.payloadLength;
}
/* Parse the Thing Name from the MQTT topic name. */
if( AwsIot_ParseThingName( pMessage->u.message.info.pTopicName,
pMessage->u.message.info.topicNameLength,
&( param.pThingName ),
&( param.thingNameLength ) ) == false )
{
IOT_GOTO_CLEANUP();
}
/* Lock the pending operations list for exclusive access. */
IotMutex_Lock( &( _AwsIotShadowPendingOperationsMutex ) );
/* Search for a matching pending operation. */
pOperationLink = IotListDouble_FindFirstMatch( &( _AwsIotShadowPendingOperations ),
NULL,
_shadowOperation_match,
&param );
/* Find and remove the first Shadow operation of the given type. */
if( pOperationLink == NULL )
{
/* Operation is not pending. It may have already been processed. Return
* without doing anything */
IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );
IotLogWarn( "Shadow %s callback received an unknown operation.",
_pAwsIotShadowOperationNames[ type ] );
IOT_GOTO_CLEANUP();
}
else
{
pOperation = IotLink_Container( _shadowOperation_t, pOperationLink, link );
/* Remove a non-waitable operation from the pending operation list.
* Waitable operations are removed by the Wait function. */
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == 0 )
{
IotListDouble_Remove( &( pOperation->link ) );
IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );
}
}
/* Check that the Shadow operation type and status. */
AwsIotShadow_Assert( pOperation->type == type );
AwsIotShadow_Assert( pOperation->status == AWS_IOT_SHADOW_STATUS_PENDING );
IotLogDebug( "Received Shadow response on topic %.*s",
pMessage->u.message.info.topicNameLength,
pMessage->u.message.info.pTopicName );
/* Parse the status from the topic name. */
status = AwsIot_ParseStatus( pMessage->u.message.info.pTopicName,
pMessage->u.message.info.topicNameLength );
switch( status )
{
case AWS_IOT_ACCEPTED:
IotLogInfo( "Shadow %s of %.*s was ACCEPTED.",
_pAwsIotShadowOperationNames[ type ],
pOperation->pSubscription->thingNameLength,
pOperation->pSubscription->pThingName );
/* Process the retrieved document for a Shadow GET. Otherwise, set
* status to success. */
if( type == SHADOW_GET )
{
pOperation->status = _processAcceptedGet( pOperation,
&( pMessage->u.message.info ) );
}
else
{
pOperation->status = AWS_IOT_SHADOW_SUCCESS;
}
break;
case AWS_IOT_REJECTED:
IotLogWarn( "Shadow %s of %.*s was REJECTED.",
_pAwsIotShadowOperationNames[ type ],
pOperation->pSubscription->thingNameLength,
pOperation->pSubscription->pThingName );
pOperation->status = _AwsIotShadow_ParseErrorDocument( pMessage->u.message.info.pPayload,
pMessage->u.message.info.payloadLength );
break;
default:
IotLogWarn( "Unknown status for %s of %.*s Shadow. Ignoring message.",
_pAwsIotShadowOperationNames[ type ],
pOperation->pSubscription->thingNameLength,
pOperation->pSubscription->pThingName );
pOperation->status = AWS_IOT_SHADOW_BAD_RESPONSE;
break;
}
/* Copy the flags from the Shadow operation. The notify function may delete the operation. */
flags = pOperation->flags;
/* Notify of operation completion. */
_notifyCompletion( pOperation );
/* For waitable operations, unlock the pending operation list mutex to allow
* the Wait function to run. */
if( ( flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == AWS_IOT_SHADOW_FLAG_WAITABLE )
{
IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );
}
/* This function has no return value and no cleanup, but uses the cleanup
* label to exit on error. */
IOT_FUNCTION_CLEANUP_BEGIN();
}
/*-----------------------------------------------------------*/
static void _deleteCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( SHADOW_DELETE, pMessage );
}
/*-----------------------------------------------------------*/
static void _getCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( SHADOW_GET, pMessage );
}
/*-----------------------------------------------------------*/
static AwsIotShadowError_t _processAcceptedGet( _shadowOperation_t * pOperation,
const IotMqttPublishInfo_t * pPublishInfo )
{
AwsIotShadowError_t status = AWS_IOT_SHADOW_SUCCESS;
/* A non-waitable operation can re-use the pointers from the publish info,
* since those are guaranteed to be in-scope throughout the user callback.
* But a waitable operation must copy the data from the publish info because
* AwsIotShadow_Wait may be called after the MQTT library frees the publish
* info. */
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == 0 )
{
pOperation->u.get.pDocument = pPublishInfo->pPayload;
pOperation->u.get.documentLength = pPublishInfo->payloadLength;
}
else
{
IotLogDebug( "Allocating new buffer for waitable Shadow GET." );
/* Parameter validation should not have allowed a NULL malloc function. */
AwsIotShadow_Assert( pOperation->u.get.mallocDocument != NULL );
/* Allocate a buffer for the retrieved document. */
pOperation->u.get.pDocument = pOperation->u.get.mallocDocument( pPublishInfo->payloadLength );
if( pOperation->u.get.pDocument == NULL )
{
IotLogError( "Failed to allocate buffer for retrieved Shadow document." );
status = AWS_IOT_SHADOW_NO_MEMORY;
}
else
{
/* Copy the retrieved document. */
( void ) memcpy( ( void * ) pOperation->u.get.pDocument,
pPublishInfo->pPayload,
pPublishInfo->payloadLength );
pOperation->u.get.documentLength = pPublishInfo->payloadLength;
}
}
return status;
}
/*-----------------------------------------------------------*/
static void _updateCallback( void * pArgument,
IotMqttCallbackParam_t * pMessage )
{
/* Silence warnings about unused parameter. */
( void ) pArgument;
_commonOperationCallback( SHADOW_UPDATE, pMessage );
}
/*-----------------------------------------------------------*/
static void _notifyCompletion( _shadowOperation_t * pOperation )
{
AwsIotShadowCallbackParam_t callbackParam = { .callbackType = ( AwsIotShadowCallbackType_t ) 0 };
_shadowSubscription_t * pSubscription = pOperation->pSubscription,
* pRemovedSubscription = NULL;
/* If the operation is waiting, post to its wait semaphore and return. */
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == AWS_IOT_SHADOW_FLAG_WAITABLE )
{
IotSemaphore_Post( &( pOperation->notify.waitSemaphore ) );
}
else
{
/* Decrement the reference count. This also removes subscriptions if the
* count reaches 0. */
IotMutex_Lock( &_AwsIotShadowSubscriptionsMutex );
_AwsIotShadow_DecrementReferences( pOperation,
pSubscription->pTopicBuffer,
&pRemovedSubscription );
IotMutex_Unlock( &_AwsIotShadowSubscriptionsMutex );
/* Set the subscription pointer used for the user callback based on whether
* a subscription was removed from the list. */
if( pRemovedSubscription != NULL )
{
pSubscription = pRemovedSubscription;
}
AwsIotShadow_Assert( pSubscription != NULL );
/* Invoke the user callback if provided. */
if( pOperation->notify.callback.function != NULL )
{
/* Set the common members of the callback parameter. */
callbackParam.callbackType = ( AwsIotShadowCallbackType_t ) pOperation->type;
callbackParam.mqttConnection = pOperation->mqttConnection;
callbackParam.u.operation.result = pOperation->status;
callbackParam.u.operation.reference = pOperation;
callbackParam.pThingName = pSubscription->pThingName;
callbackParam.thingNameLength = pSubscription->thingNameLength;
/* Set the members of the callback parameter for a received document. */
if( pOperation->type == SHADOW_GET )
{
callbackParam.u.operation.get.pDocument = pOperation->u.get.pDocument;
callbackParam.u.operation.get.documentLength = pOperation->u.get.documentLength;
}
pOperation->notify.callback.function( pOperation->notify.callback.pCallbackContext,
&callbackParam );
}
/* Destroy a removed subscription. */
if( pRemovedSubscription != NULL )
{
_AwsIotShadow_DestroySubscription( pRemovedSubscription );
}
_AwsIotShadow_DestroyOperation( pOperation );
}
}
/*-----------------------------------------------------------*/
static AwsIotShadowError_t _findSubscription( const char * pThingName,
size_t thingNameLength,
char * pTopicBuffer,
uint16_t operationTopicLength,
_shadowOperation_t * pOperation,
bool * pFreeTopicBuffer )
{
AwsIotShadowError_t status = AWS_IOT_SHADOW_SUCCESS;
_shadowSubscription_t * pSubscription = NULL;
/* Lookup table for Shadow operation callbacks. */
const AwsIotMqttCallbackFunction_t shadowCallbacks[ SHADOW_OPERATION_COUNT ] =
{
_deleteCallback,
_getCallback,
_updateCallback
};
/* Lock the subscriptions mutex for exclusive access. */
IotMutex_Lock( &_AwsIotShadowSubscriptionsMutex );
/* Check for an existing subscription. This function will attempt to allocate
* a new subscription if not found. */
pSubscription = _AwsIotShadow_FindSubscription( pThingName,
thingNameLength,
true );
if( pSubscription == NULL )
{
status = AWS_IOT_SHADOW_NO_MEMORY;
}
else
{
/* Ensure that the subscription Thing Name matches. */
AwsIotShadow_Assert( pSubscription != NULL );
AwsIotShadow_Assert( pSubscription->thingNameLength == thingNameLength );
AwsIotShadow_Assert( strncmp( pSubscription->pThingName,
pThingName,
thingNameLength ) == 0 );
/* Set the subscription object for the Shadow operation. */
pOperation->pSubscription = pSubscription;
/* Assign the topic buffer to the subscription to use for unsubscribing if
* the subscription has no topic buffer. */
if( pSubscription->pTopicBuffer == NULL )
{
pSubscription->pTopicBuffer = pTopicBuffer;
/* Don't free the topic buffer if it was allocated to the subscription. */
*pFreeTopicBuffer = false;
}
else
{
*pFreeTopicBuffer = true;
}
/* Increment the reference count for this Shadow operation's
* subscriptions. */
status = _AwsIotShadow_IncrementReferences( pOperation,
pTopicBuffer,
operationTopicLength,
shadowCallbacks[ pOperation->type ] );
if( status != AWS_IOT_SHADOW_SUCCESS )
{
/* Failed to add subscriptions for a Shadow operation. The reference
* count was not incremented. Check if this subscription should be
* deleted. */
_AwsIotShadow_RemoveSubscription( pSubscription, NULL );
}
}
/* Unlock the Shadow subscription list mutex. */
IotMutex_Unlock( &_AwsIotShadowSubscriptionsMutex );
return status;
}
/*-----------------------------------------------------------*/
AwsIotShadowError_t _AwsIotShadow_CreateOperation( _shadowOperation_t ** pNewOperation,
_shadowOperationType_t type,
uint32_t flags,
const AwsIotShadowCallbackInfo_t * pCallbackInfo )
{
IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_SUCCESS );
_shadowOperation_t * pOperation = NULL;
IotLogDebug( "Creating operation record for Shadow %s.",
_pAwsIotShadowOperationNames[ type ] );
/* Allocate memory for a new Shadow operation. */
pOperation = AwsIotShadow_MallocOperation( sizeof( _shadowOperation_t ) );
if( pOperation == NULL )
{
IotLogError( "Failed to allocate memory for Shadow %s.",
_pAwsIotShadowOperationNames[ type ] );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NO_MEMORY );
}
/* Clear the operation data. */
( void ) memset( pOperation, 0x00, sizeof( _shadowOperation_t ) );
/* Check if the waitable flag is set. If it is, create a semaphore to
* wait on. */
if( ( flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == AWS_IOT_SHADOW_FLAG_WAITABLE )
{
if( IotSemaphore_Create( &( pOperation->notify.waitSemaphore ), 0, 1 ) == false )
{
IotLogError( "Failed to create semaphore for waitable Shadow %s.",
_pAwsIotShadowOperationNames[ type ] );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NO_MEMORY );
}
}
else
{
/* If the waitable flag isn't set but a callback is, copy the callback
* information. */
if( pCallbackInfo != NULL )
{
pOperation->notify.callback = *pCallbackInfo;
}
}
/* Set the remaining common members of the Shadow operation. */
pOperation->type = type;
pOperation->flags = flags;
pOperation->status = AWS_IOT_SHADOW_STATUS_PENDING;
IOT_FUNCTION_CLEANUP_BEGIN();
if( status != AWS_IOT_SHADOW_SUCCESS )
{
if( pOperation != NULL )
{
AwsIotShadow_FreeOperation( pOperation );
}
}
else
{
/* Set the output parameter. */
*pNewOperation = pOperation;
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/
void _AwsIotShadow_DestroyOperation( void * pData )
{
_shadowOperation_t * pOperation = ( _shadowOperation_t * ) pData;
/* The Shadow operation pointer must not be NULL. */
AwsIotShadow_Assert( pOperation != NULL );
IotLogDebug( "Destroying Shadow operation %s.",
_pAwsIotShadowOperationNames[ pOperation->type ] );
/* Check if a wait semaphore was created for this operation. */
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_WAITABLE ) == AWS_IOT_SHADOW_FLAG_WAITABLE )
{
/* Destroy the wait semaphore */
IotSemaphore_Destroy( &( pOperation->notify.waitSemaphore ) );
}
/* If this is a Shadow update, free any allocated client token. */
if( ( pOperation->type == SHADOW_UPDATE ) &&
( pOperation->u.update.pClientToken != NULL ) )
{
AwsIotShadow_Assert( pOperation->u.update.clientTokenLength > 0 );
AwsIotShadow_FreeString( ( void * ) ( pOperation->u.update.pClientToken ) );
}
/* Free the memory used to hold operation data. */
AwsIotShadow_FreeOperation( pOperation );
}
/*-----------------------------------------------------------*/
AwsIotShadowError_t _AwsIotShadow_GenerateShadowTopic( _shadowOperationType_t type,
const char * pThingName,
size_t thingNameLength,
char ** pTopicBuffer,
uint16_t * pOperationTopicLength )
{
AwsIotShadowError_t status = AWS_IOT_SHADOW_SUCCESS;
AwsIotTopicInfo_t topicInfo = { 0 };
/* Lookup table for Shadow operation strings. */
const char * const pOperationString[ SHADOW_OPERATION_COUNT ] =
{
SHADOW_DELETE_OPERATION_STRING, /* Shadow delete operation. */
SHADOW_GET_OPERATION_STRING, /* Shadow get operation. */
SHADOW_UPDATE_OPERATION_STRING /* Shadow update operation. */
};
/* Lookup table for Shadow operation string lengths. */
const uint16_t pOperationStringLength[ SHADOW_OPERATION_COUNT ] =
{
SHADOW_DELETE_OPERATION_STRING_LENGTH, /* Shadow delete operation. */
SHADOW_GET_OPERATION_STRING_LENGTH, /* Shadow get operation. */
SHADOW_UPDATE_OPERATION_STRING_LENGTH /* Shadow update operation. */
};
/* Only Shadow delete, get, and update operation types should be passed to this
* function. */
AwsIotShadow_Assert( ( type == SHADOW_DELETE ) ||
( type == SHADOW_GET ) ||
( type == SHADOW_UPDATE ) );
/* Set the members needed to generate an operation topic. */
topicInfo.pThingName = pThingName;
topicInfo.thingNameLength = thingNameLength;
topicInfo.pOperationName = pOperationString[ type ];
topicInfo.operationNameLength = pOperationStringLength[ type ];
topicInfo.longestSuffixLength = SHADOW_LONGEST_SUFFIX_LENGTH;
topicInfo.mallocString = AwsIotShadow_MallocString;
if( AwsIot_GenerateOperationTopic( &topicInfo,
pTopicBuffer,
pOperationTopicLength ) == false )
{
status = AWS_IOT_SHADOW_NO_MEMORY;
}
return status;
}
/*-----------------------------------------------------------*/
AwsIotShadowError_t _AwsIotShadow_ProcessOperation( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
_shadowOperation_t * pOperation,
const AwsIotShadowDocumentInfo_t * pDocumentInfo )
{
IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_STATUS_PENDING );
IotMqttError_t publishStatus = IOT_MQTT_STATUS_PENDING;
char * pTopicBuffer = NULL;
uint16_t operationTopicLength = 0;
bool freeTopicBuffer = true;
IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
IotLogDebug( "Processing Shadow operation %s for Thing %.*s.",
_pAwsIotShadowOperationNames[ pOperation->type ],
thingNameLength,
pThingName );
/* Set the operation's MQTT connection. */
pOperation->mqttConnection = mqttConnection;
/* Generate the operation topic buffer. */
status = _AwsIotShadow_GenerateShadowTopic( pOperation->type,
pThingName,
thingNameLength,
&pTopicBuffer,
&operationTopicLength );
if( status != AWS_IOT_SHADOW_SUCCESS )
{
IotLogError( "No memory for Shadow operation topic buffer." );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_NO_MEMORY );
}
/* Get a subscription object for this Shadow operation. */
status = _findSubscription( pThingName,
thingNameLength,
pTopicBuffer,
operationTopicLength,
pOperation,
&freeTopicBuffer );
if( status != AWS_IOT_SHADOW_SUCCESS )
{
/* No subscription was found and no subscription could be allocated. */
IOT_GOTO_CLEANUP();
}
/* Set the operation topic name. */
publishInfo.pTopicName = pTopicBuffer;
publishInfo.topicNameLength = operationTopicLength;
IotLogDebug( "Shadow %s message will be published to topic %.*s",
_pAwsIotShadowOperationNames[ pOperation->type ],
publishInfo.topicNameLength,
publishInfo.pTopicName );
/* Set the document info if this operation is not a Shadow DELETE. */
if( pOperation->type != SHADOW_DELETE )
{
publishInfo.qos = pDocumentInfo->qos;
publishInfo.retryLimit = pDocumentInfo->retryLimit;
publishInfo.retryMs = pDocumentInfo->retryMs;
IotLogDebug( "Shadow %s message will be published at QoS %d with "
"retryLimit %d and retryMs %llu.",
_pAwsIotShadowOperationNames[ pOperation->type ],
publishInfo.qos,
publishInfo.retryLimit,
publishInfo.retryMs );
}
/* Set the PUBLISH payload to the update document for Shadow UPDATE. */
if( pOperation->type == SHADOW_UPDATE )
{
publishInfo.pPayload = pDocumentInfo->u.update.pUpdateDocument;
publishInfo.payloadLength = pDocumentInfo->u.update.updateDocumentLength;
}
/* Set the PUBLISH payload to an empty string for Shadow DELETE and GET,
* per the Shadow spec. */
else
{
publishInfo.pPayload = "";
publishInfo.payloadLength = 0;
}
/* Add Shadow operation to the pending operations list. */
IotMutex_Lock( &( _AwsIotShadowPendingOperationsMutex ) );
IotListDouble_InsertHead( &( _AwsIotShadowPendingOperations ),
&( pOperation->link ) );
IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );
/* Publish to the Shadow topic name. */
publishStatus = IotMqtt_PublishSync( pOperation->mqttConnection,
&publishInfo,
0,
_AwsIotShadowMqttTimeoutMs );
/* Check for errors from the MQTT publish. */
if( publishStatus != IOT_MQTT_SUCCESS )
{
IotLogError( "Failed to publish MQTT message to %s %.*s Shadow, error %s.",
_pAwsIotShadowOperationNames[ pOperation->type ],
thingNameLength,
pThingName,
IotMqtt_strerror( publishStatus ) );
/* Convert the MQTT "NO MEMORY" error to a Shadow "NO MEMORY" error. */
status = SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( publishStatus );
/* If the "keep subscriptions" flag is not set, decrement the reference
* count. */
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )
{
IotMutex_Lock( &_AwsIotShadowSubscriptionsMutex );
_AwsIotShadow_DecrementReferences( pOperation,
pTopicBuffer,
NULL );
IotMutex_Unlock( &_AwsIotShadowSubscriptionsMutex );
}
/* Remove Shadow operation from the pending operations list. */
IotMutex_Lock( &( _AwsIotShadowPendingOperationsMutex ) );
IotListDouble_Remove( &( pOperation->link ) );
IotMutex_Unlock( &( _AwsIotShadowPendingOperationsMutex ) );
}
else
{
IotLogDebug( "Shadow %s PUBLISH message successfully sent.",
_pAwsIotShadowOperationNames[ pOperation->type ] );
}
IOT_FUNCTION_CLEANUP_BEGIN();
/* Free the topic buffer used by this function if it was not assigned to a
* subscription. */
if( ( freeTopicBuffer == true ) && ( pTopicBuffer != NULL ) )
{
AwsIotShadow_FreeString( pTopicBuffer );
}
/* Destroy the Shadow operation on failure. */
if( status != AWS_IOT_SHADOW_SUCCESS )
{
_AwsIotShadow_DestroyOperation( pOperation );
}
else
{
/* Convert successful return code to "status pending", as the Shadow
* library is now waiting for a response from the service. */
status = AWS_IOT_SHADOW_STATUS_PENDING;
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,195 @@
/*
* AWS IoT Shadow V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_shadow_parser.c
* @brief Implements JSON parsing functions of the Shadow library.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdlib.h>
#include <string.h>
/* Shadow internal include. */
#include "private/aws_iot_shadow_internal.h"
/* Error handling include. */
#include "iot_error.h"
/* AWS Parser include. */
#include "aws_iot_doc_parser.h"
/*-----------------------------------------------------------*/
/**
* @brief The JSON key for the error code in a Shadow error document.
*/
#define ERROR_DOCUMENT_CODE_KEY "code"
/**
* @brief The length of #ERROR_DOCUMENT_CODE_KEY.
*/
#define ERROR_DOCUMENT_CODE_KEY_LENGTH ( sizeof( ERROR_DOCUMENT_CODE_KEY ) - 1 )
/**
* @brief The JSON key for the error message in a Shadow error document.
*/
#define ERROR_DOCUMENT_MESSAGE_KEY "message"
/**
* @brief The length of #ERROR_DOCUMENT_MESSAGE_KEY.
*/
#define ERROR_DOCUMENT_MESSAGE_KEY_LENGTH ( sizeof( ERROR_DOCUMENT_MESSAGE_KEY ) - 1 )
/*-----------------------------------------------------------*/
/**
* @brief Converts a `unsigned long` to an `AwsIotShadowError_t`.
*
* @param[in] code A value between 400 and 500 to convert.
*
* @return A corresponding #AwsIotShadowError_t; #AWS_IOT_SHADOW_BAD_RESPONSE
* if `code` is unknown.
*/
static AwsIotShadowError_t _codeToShadowStatus( uint32_t code );
/*-----------------------------------------------------------*/
static AwsIotShadowError_t _codeToShadowStatus( uint32_t code )
{
AwsIotShadowError_t errorCode = AWS_IOT_SHADOW_STATUS_PENDING;
/* Convert the Shadow response code to an AwsIotShadowError_t. */
switch( code )
{
case 400UL:
errorCode = AWS_IOT_SHADOW_BAD_REQUEST;
break;
case 401UL:
errorCode = AWS_IOT_SHADOW_UNAUTHORIZED;
break;
case 403UL:
errorCode = AWS_IOT_SHADOW_FORBIDDEN;
break;
case 404UL:
errorCode = AWS_IOT_SHADOW_NOT_FOUND;
break;
case 409UL:
errorCode = AWS_IOT_SHADOW_CONFLICT;
break;
case 413UL:
errorCode = AWS_IOT_SHADOW_TOO_LARGE;
break;
case 415UL:
errorCode = AWS_IOT_SHADOW_UNSUPPORTED;
break;
case 429UL:
errorCode = AWS_IOT_SHADOW_TOO_MANY_REQUESTS;
break;
case 500UL:
errorCode = AWS_IOT_SHADOW_SERVER_ERROR;
break;
default:
errorCode = AWS_IOT_SHADOW_BAD_RESPONSE;
break;
}
return errorCode;
}
/*-----------------------------------------------------------*/
AwsIotShadowError_t _AwsIotShadow_ParseErrorDocument( const char * pErrorDocument,
size_t errorDocumentLength )
{
IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_STATUS_PENDING );
const char * pCode = NULL, * pMessage = NULL;
size_t codeLength = 0, messageLength = 0;
uint32_t code = 0;
/* Parse the code from the error document. */
if( AwsIotDocParser_FindValue( pErrorDocument,
errorDocumentLength,
ERROR_DOCUMENT_CODE_KEY,
ERROR_DOCUMENT_CODE_KEY_LENGTH,
&pCode,
&codeLength ) == false )
{
/* Error parsing JSON document, or no "code" key was found. */
IotLogWarn( "Failed to parse code from error document.\n%.*s",
errorDocumentLength,
pErrorDocument );
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_RESPONSE );
}
/* Code must be in error document. */
AwsIotShadow_Assert( ( pCode > pErrorDocument ) &&
( pCode + codeLength < pErrorDocument + errorDocumentLength ) );
/* Convert the code to an unsigned integer value. */
code = ( uint32_t ) strtoul( pCode, NULL, 10 );
/* Parse the error message and print it. An error document must always contain
* a message. */
if( AwsIotDocParser_FindValue( pErrorDocument,
errorDocumentLength,
ERROR_DOCUMENT_MESSAGE_KEY,
ERROR_DOCUMENT_MESSAGE_KEY_LENGTH,
&pMessage,
&messageLength ) == true )
{
IotLogWarn( "Code %lu: %.*s.",
code,
messageLength,
pMessage );
}
else
{
IotLogWarn( "Code %lu; failed to parse message from error document.\n%.*s",
code,
errorDocumentLength,
pErrorDocument );
/* An error document must contain a message; if it does not, then it is invalid. */
IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_SHADOW_BAD_RESPONSE );
}
/* Convert a successfully parsed JSON code to a Shadow status. */
status = _codeToShadowStatus( code );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,160 @@
/*
* AWS IoT Shadow V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_shadow_static_memory.c
* @brief Implementation of Shadow static memory functions.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* This file should only be compiled if dynamic memory allocation is forbidden. */
#if IOT_STATIC_MEMORY_ONLY == 1
/* Standard includes. */
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
/* Static memory include. */
#include "iot_static_memory.h"
/* Shadow internal include. */
#include "private/aws_iot_shadow_internal.h"
/*-----------------------------------------------------------*/
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Provide default values for undefined configuration constants.
*/
#ifndef AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS
#define AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS ( 10 )
#endif
#ifndef AWS_IOT_SHADOW_SUBSCRIPTIONS
#define AWS_IOT_SHADOW_SUBSCRIPTIONS ( 2 )
#endif
/** @endcond */
/* Validate static memory configuration settings. */
#if AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS <= 0
#error "AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS cannot be 0 or negative."
#endif
#if AWS_IOT_SHADOW_SUBSCRIPTIONS <= 0
#error "AWS_IOT_SHADOW_SUBSCRIPTIONS cannot be 0 or negative."
#endif
/**
* @brief The size of a static memory Shadow subscription.
*
* Since the pThingName member of #_shadowSubscription_t is variable-length,
* the constant `AWS_IOT_MAX_THING_NAME_LENGTH` is used for the length of
* #_shadowSubscription_t.pThingName.
*/
#define SHADOW_SUBSCRIPTION_SIZE ( sizeof( _shadowSubscription_t ) + AWS_IOT_MAX_THING_NAME_LENGTH )
/*-----------------------------------------------------------*/
/*
* Static memory buffers and flags, allocated and zeroed at compile-time.
*/
static uint32_t _pInUseShadowOperations[ AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS ] = { 0U }; /**< @brief Shadow operation in-use flags. */
static _shadowOperation_t _pShadowOperations[ AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS ] = { { .link = { 0 } } }; /**< @brief Shadow operations. */
static uint32_t _pInUseShadowSubscriptions[ AWS_IOT_SHADOW_SUBSCRIPTIONS ] = { 0U }; /**< @brief Shadow subscription in-use flags. */
static char _pShadowSubscriptions[ AWS_IOT_SHADOW_SUBSCRIPTIONS ][ SHADOW_SUBSCRIPTION_SIZE ] = { { 0 } }; /**< @brief Shadow subscriptions. */
/*-----------------------------------------------------------*/
void * AwsIotShadow_MallocOperation( size_t size )
{
int32_t freeIndex = -1;
void * pNewOperation = NULL;
/* Check size argument. */
if( size == sizeof( _shadowOperation_t ) )
{
/* Find a free Shadow operation. */
freeIndex = IotStaticMemory_FindFree( _pInUseShadowOperations,
AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS );
if( freeIndex != -1 )
{
pNewOperation = &( _pShadowOperations[ freeIndex ] );
}
}
return pNewOperation;
}
/*-----------------------------------------------------------*/
void AwsIotShadow_FreeOperation( void * ptr )
{
/* Return the in-use Shadow operation. */
IotStaticMemory_ReturnInUse( ptr,
_pShadowOperations,
_pInUseShadowOperations,
AWS_IOT_SHADOW_MAX_IN_PROGRESS_OPERATIONS,
sizeof( _shadowOperation_t ) );
}
/*-----------------------------------------------------------*/
void * AwsIotShadow_MallocSubscription( size_t size )
{
int32_t freeIndex = -1;
void * pNewSubscription = NULL;
if( size <= SHADOW_SUBSCRIPTION_SIZE )
{
/* Get the index of a free Shadow subscription. */
freeIndex = IotStaticMemory_FindFree( _pInUseShadowSubscriptions,
AWS_IOT_SHADOW_SUBSCRIPTIONS );
if( freeIndex != -1 )
{
pNewSubscription = &( _pShadowSubscriptions[ freeIndex ][ 0 ] );
}
}
return pNewSubscription;
}
/*-----------------------------------------------------------*/
void AwsIotShadow_FreeSubscription( void * ptr )
{
/* Return the in-use Shadow subscription. */
IotStaticMemory_ReturnInUse( ptr,
_pShadowSubscriptions,
_pInUseShadowSubscriptions,
AWS_IOT_SHADOW_SUBSCRIPTIONS,
SHADOW_SUBSCRIPTION_SIZE );
}
/*-----------------------------------------------------------*/
#endif

View file

@ -0,0 +1,512 @@
/*
* AWS IoT Shadow V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_shadow_subscription.c
* @brief Implements functions for interacting with the Shadow library's
* subscription list.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* Shadow internal include. */
#include "private/aws_iot_shadow_internal.h"
/* Error handling include. */
#include "iot_error.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
/* MQTT include. */
#include "iot_mqtt.h"
/*-----------------------------------------------------------*/
/**
* @brief Match two #_shadowSubscription_t by Thing Name.
*
* @param[in] pSubscriptionLink Pointer to the link member of a #_shadowSubscription_t
* containing the Thing Name to check.
* @param[in] pMatch Pointer to an `AwsIotThingName_t`.
*
* @return `true` if the Thing Names match; `false` otherwise.
*/
static bool _shadowSubscription_match( const IotLink_t * pSubscriptionLink,
void * pMatch );
/*-----------------------------------------------------------*/
/**
* @brief List of active Shadow subscriptions objects.
*/
IotListDouble_t _AwsIotShadowSubscriptions = { 0 };
/**
* @brief Protects #_AwsIotShadowSubscriptions from concurrent access.
*/
IotMutex_t _AwsIotShadowSubscriptionsMutex;
/*-----------------------------------------------------------*/
static bool _shadowSubscription_match( const IotLink_t * pSubscriptionLink,
void * pMatch )
{
bool match = false;
/* Because this function is called from a container function, the given link
* must never be NULL. */
AwsIotShadow_Assert( pSubscriptionLink != NULL );
const _shadowSubscription_t * pSubscription = IotLink_Container( _shadowSubscription_t,
pSubscriptionLink,
link );
const AwsIotThingName_t * pThingName = ( AwsIotThingName_t * ) pMatch;
if( pThingName->thingNameLength == pSubscription->thingNameLength )
{
/* Check for matching Thing Names. */
match = ( strncmp( pThingName->pThingName,
pSubscription->pThingName,
pThingName->thingNameLength ) == 0 );
}
return match;
}
/*-----------------------------------------------------------*/
_shadowSubscription_t * _AwsIotShadow_FindSubscription( const char * pThingName,
size_t thingNameLength,
bool createIfNotFound )
{
_shadowSubscription_t * pSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
AwsIotThingName_t thingName = { 0 };
thingName.pThingName = pThingName;
thingName.thingNameLength = thingNameLength;
/* Search the list for an existing subscription for Thing Name. */
pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotShadowSubscriptions ),
NULL,
_shadowSubscription_match,
&thingName );
/* Check if a subscription was found. */
if( pSubscriptionLink == NULL )
{
if( createIfNotFound == true )
{
/* No subscription found. Allocate a new subscription. */
pSubscription = AwsIotShadow_MallocSubscription( sizeof( _shadowSubscription_t ) + thingNameLength );
if( pSubscription != NULL )
{
/* Clear the new subscription. */
( void ) memset( pSubscription, 0x00, sizeof( _shadowSubscription_t ) + thingNameLength );
/* Set the Thing Name length and copy the Thing Name into the new subscription. */
pSubscription->thingNameLength = thingNameLength;
( void ) memcpy( pSubscription->pThingName, pThingName, thingNameLength );
/* Add the new subscription to the subscription list. */
IotListDouble_InsertHead( &( _AwsIotShadowSubscriptions ),
&( pSubscription->link ) );
IotLogDebug( "Created new Shadow subscriptions object for %.*s.",
thingNameLength,
pThingName );
}
else
{
IotLogError( "Failed to allocate memory for %.*s Shadow subscriptions.",
thingNameLength,
pThingName );
}
}
}
else
{
IotLogDebug( "Found existing Shadow subscriptions object for %.*s.",
thingNameLength,
pThingName );
pSubscription = IotLink_Container( _shadowSubscription_t, pSubscriptionLink, link );
}
return pSubscription;
}
/*-----------------------------------------------------------*/
void _AwsIotShadow_RemoveSubscription( _shadowSubscription_t * pSubscription,
_shadowSubscription_t ** pRemovedSubscription )
{
int32_t i = 0;
bool removeSubscription = true;
IotLogDebug( "Checking if subscription object for %.*s can be removed.",
pSubscription->thingNameLength,
pSubscription->pThingName );
/* Check for active operations. If any Shadow operation's subscription
* reference count is not 0, then the subscription cannot be removed. */
for( i = 0; i < SHADOW_OPERATION_COUNT; i++ )
{
if( pSubscription->references[ i ] > 0 )
{
IotLogDebug( "Reference count %ld for %.*s subscription object. "
"Subscription cannot be removed yet.",
( long int ) pSubscription->references[ i ],
pSubscription->thingNameLength,
pSubscription->pThingName );
removeSubscription = false;
}
else if( pSubscription->references[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
IotLogDebug( "Subscription object for %.*s has persistent subscriptions. "
"Subscription will not be removed.",
pSubscription->thingNameLength,
pSubscription->pThingName );
removeSubscription = false;
}
if( removeSubscription == false )
{
break;
}
}
/* Check for active subscriptions. If any Shadow callbacks are active, then the
* subscription cannot be removed. */
if( removeSubscription == true )
{
for( i = 0; i < SHADOW_CALLBACK_COUNT; i++ )
{
if( pSubscription->callbacks[ i ].function != NULL )
{
IotLogDebug( "Found active Shadow %s callback for %.*s subscription object. "
"Subscription cannot be removed yet.",
_pAwsIotShadowCallbackNames[ i ],
pSubscription->thingNameLength,
pSubscription->pThingName );
removeSubscription = false;
break;
}
}
}
/* Remove the subscription if unused. */
if( removeSubscription == true )
{
/* No Shadow operation subscription references or active Shadow callbacks.
* Remove the subscription object. */
IotListDouble_Remove( &( pSubscription->link ) );
IotLogDebug( "Removed subscription object for %.*s.",
pSubscription->thingNameLength,
pSubscription->pThingName );
/* If the caller requested the removed subscription, set the output parameter.
* Otherwise, free the memory used by the subscription. */
if( pRemovedSubscription != NULL )
{
*pRemovedSubscription = pSubscription;
}
else
{
_AwsIotShadow_DestroySubscription( pSubscription );
}
}
}
/*-----------------------------------------------------------*/
void _AwsIotShadow_DestroySubscription( void * pData )
{
_shadowSubscription_t * pSubscription = ( _shadowSubscription_t * ) pData;
/* Free the topic buffer if allocated. */
if( pSubscription->pTopicBuffer != NULL )
{
AwsIotShadow_FreeString( pSubscription->pTopicBuffer );
}
/* Free memory used by subscription. */
AwsIotShadow_FreeSubscription( pSubscription );
}
/*-----------------------------------------------------------*/
AwsIotShadowError_t _AwsIotShadow_IncrementReferences( _shadowOperation_t * pOperation,
char * pTopicBuffer,
uint16_t operationTopicLength,
AwsIotMqttCallbackFunction_t callback )
{
IOT_FUNCTION_ENTRY( AwsIotShadowError_t, AWS_IOT_SHADOW_SUCCESS );
const _shadowOperationType_t type = pOperation->type;
_shadowSubscription_t * pSubscription = pOperation->pSubscription;
IotMqttError_t subscriptionStatus = IOT_MQTT_STATUS_PENDING;
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
/* Do nothing if this operation has persistent subscriptions. */
if( pSubscription->references[ type ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
IotLogDebug( "Shadow %s for %.*s has persistent subscriptions. Reference "
"count will not be incremented.",
_pAwsIotShadowOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName );
IOT_GOTO_CLEANUP();
}
/* When persistent subscriptions are not present, the reference count must
* not be negative. */
AwsIotShadow_Assert( pSubscription->references[ type ] >= 0 );
/* Check if there are any existing references for this operation. */
if( pSubscription->references[ type ] == 0 )
{
/* Set the parameters needed to add subscriptions. */
subscriptionInfo.mqttConnection = pOperation->mqttConnection;
subscriptionInfo.callbackFunction = callback;
subscriptionInfo.timeout = _AwsIotShadowMqttTimeoutMs;
subscriptionInfo.pTopicFilterBase = pTopicBuffer;
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
subscriptionStatus = AwsIot_ModifySubscriptions( IotMqtt_SubscribeSync,
&subscriptionInfo );
/* Convert MQTT return code to Shadow return code. */
status = SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( subscriptionStatus );
if( status != AWS_IOT_SHADOW_SUCCESS )
{
IOT_GOTO_CLEANUP();
}
}
/* Increment the number of subscription references for this operation when
* the keep subscriptions flag is not set. */
if( ( pOperation->flags & AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS ) == 0 )
{
( pSubscription->references[ type ] )++;
IotLogDebug( "Shadow %s subscriptions for %.*s now has count %d.",
_pAwsIotShadowOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName,
pSubscription->references[ type ] );
}
/* Otherwise, set the persistent subscriptions flag. */
else
{
pSubscription->references[ type ] = AWS_IOT_PERSISTENT_SUBSCRIPTION;
IotLogDebug( "Set persistent subscriptions flag for Shadow %s of %.*s.",
_pAwsIotShadowOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName );
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
void _AwsIotShadow_DecrementReferences( _shadowOperation_t * pOperation,
char * pTopicBuffer,
_shadowSubscription_t ** pRemovedSubscription )
{
const _shadowOperationType_t type = pOperation->type;
_shadowSubscription_t * pSubscription = pOperation->pSubscription;
uint16_t operationTopicLength = 0;
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
/* Do nothing if this Shadow operation has persistent subscriptions. */
if( pSubscription->references[ type ] != AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
/* Decrement the number of subscription references for this operation.
* Ensure that it's positive. */
( pSubscription->references[ type ] )--;
AwsIotShadow_Assert( pSubscription->references[ type ] >= 0 );
/* Check if the number of references has reached 0. */
if( pSubscription->references[ type ] == 0 )
{
IotLogDebug( "Reference count for %.*s %s is 0. Unsubscribing.",
pSubscription->thingNameLength,
pSubscription->pThingName,
_pAwsIotShadowOperationNames[ type ] );
/* Subscription must have a topic buffer. */
AwsIotShadow_Assert( pSubscription->pTopicBuffer != NULL );
/* Generate the prefix of the Shadow topic. This function will not
* fail when given a buffer. */
( void ) _AwsIotShadow_GenerateShadowTopic( ( _shadowOperationType_t ) type,
pSubscription->pThingName,
pSubscription->thingNameLength,
&( pSubscription->pTopicBuffer ),
&operationTopicLength );
/* Set the parameters needed to remove subscriptions. */
subscriptionInfo.mqttConnection = pOperation->mqttConnection;
subscriptionInfo.timeout = _AwsIotShadowMqttTimeoutMs;
subscriptionInfo.pTopicFilterBase = pTopicBuffer;
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
( void ) AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,
&subscriptionInfo );
}
/* Check if this subscription should be deleted. */
_AwsIotShadow_RemoveSubscription( pSubscription,
pRemovedSubscription );
}
else
{
IotLogDebug( "Shadow %s for %.*s has persistent subscriptions. Reference "
"count will not be decremented.",
_pAwsIotShadowOperationNames[ type ],
pSubscription->thingNameLength,
pSubscription->pThingName );
}
}
/*-----------------------------------------------------------*/
AwsIotShadowError_t AwsIotShadow_RemovePersistentSubscriptions( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
uint32_t flags )
{
int32_t i = 0;
uint16_t operationTopicLength = 0;
AwsIotShadowError_t status = AWS_IOT_SHADOW_STATUS_PENDING;
IotMqttError_t unsubscribeStatus = IOT_MQTT_STATUS_PENDING;
AwsIotSubscriptionInfo_t subscriptionInfo = { 0 };
_shadowSubscription_t * pSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
AwsIotThingName_t thingName = { 0 };
thingName.pThingName = pThingName;
thingName.thingNameLength = thingNameLength;
IotLogInfo( "Removing persistent subscriptions for %.*s.",
thingNameLength,
pThingName );
IotMutex_Lock( &( _AwsIotShadowSubscriptionsMutex ) );
/* Search the list for an existing subscription for Thing Name. */
pSubscriptionLink = IotListDouble_FindFirstMatch( &( _AwsIotShadowSubscriptions ),
NULL,
_shadowSubscription_match,
&thingName );
/* Unsubscribe from operation subscriptions if found. */
if( pSubscriptionLink != NULL )
{
IotLogDebug( "Found subscription object for %.*s. Checking for persistent "
"subscriptions to remove.",
thingNameLength,
pThingName );
pSubscription = IotLink_Container( _shadowSubscription_t, pSubscriptionLink, link );
for( i = 0; i < SHADOW_OPERATION_COUNT; i++ )
{
if( ( flags & ( 0x1UL << i ) ) != 0 )
{
IotLogDebug( "Removing %.*s %s persistent subscriptions.",
thingNameLength,
pThingName,
_pAwsIotShadowOperationNames[ i ] );
/* Subscription must have a topic buffer. */
AwsIotShadow_Assert( pSubscription->pTopicBuffer != NULL );
if( pSubscription->references[ i ] == AWS_IOT_PERSISTENT_SUBSCRIPTION )
{
/* Generate the prefix of the Shadow topic. This function will not
* fail when given a buffer. */
( void ) _AwsIotShadow_GenerateShadowTopic( ( _shadowOperationType_t ) i,
pThingName,
thingNameLength,
&( pSubscription->pTopicBuffer ),
&operationTopicLength );
/* Set the parameters needed to remove subscriptions. */
subscriptionInfo.mqttConnection = mqttConnection;
subscriptionInfo.timeout = _AwsIotShadowMqttTimeoutMs;
subscriptionInfo.pTopicFilterBase = pSubscription->pTopicBuffer;
subscriptionInfo.topicFilterBaseLength = operationTopicLength;
unsubscribeStatus = AwsIot_ModifySubscriptions( IotMqtt_UnsubscribeSync,
&subscriptionInfo );
/* Convert MQTT return code to Shadow return code. */
status = SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( unsubscribeStatus );
if( status != AWS_IOT_SHADOW_SUCCESS )
{
break;
}
/* Clear the persistent subscriptions flag and check if the
* subscription can be removed. */
pSubscription->references[ i ] = 0;
_AwsIotShadow_RemoveSubscription( pSubscription, NULL );
}
else
{
IotLogDebug( "%.*s %s does not have persistent subscriptions.",
thingNameLength,
pThingName,
_pAwsIotShadowOperationNames[ i ] );
}
}
}
}
else
{
IotLogWarn( "No subscription object found for %.*s",
thingNameLength,
pThingName );
}
IotMutex_Unlock( &( _AwsIotShadowSubscriptionsMutex ) );
return status;
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,565 @@
/*
* AWS IoT Shadow V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file aws_iot_shadow_internal.h
* @brief Internal header of Shadow library. This header should not be included in
* typical application code.
*/
#ifndef AWS_IOT_SHADOW_INTERNAL_H_
#define AWS_IOT_SHADOW_INTERNAL_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Linear containers (lists and queues) include. */
#include "iot_linear_containers.h"
/* Platform layer types include. */
#include "types/iot_platform_types.h"
/* Shadow include. */
#include "aws_iot_shadow.h"
/* AWS IoT include. */
#include "aws_iot.h"
/**
* @def AwsIotShadow_Assert( expression )
* @brief Assertion macro for the Shadow library.
*
* Set @ref AWS_IOT_SHADOW_ENABLE_ASSERTS to `1` to enable assertions in the Shadow
* library.
*
* @param[in] expression Expression to be evaluated.
*/
#if AWS_IOT_SHADOW_ENABLE_ASSERTS == 1
#ifndef AwsIotShadow_Assert
#ifdef Iot_DefaultAssert
#define AwsIotShadow_Assert( expression ) Iot_DefaultAssert( expression )
#else
#error "Asserts are enabled for Shadow, but AwsIotShadow_Assert is not defined"
#endif
#endif
#else /* if AWS_IOT_SHADOW_ENABLE_ASSERTS == 1 */
#define AwsIotShadow_Assert( expression )
#endif /* if AWS_IOT_SHADOW_ENABLE_ASSERTS == 1 */
/* Configure logs for Shadow functions. */
#ifdef AWS_IOT_LOG_LEVEL_SHADOW
#define LIBRARY_LOG_LEVEL AWS_IOT_LOG_LEVEL_SHADOW
#else
#ifdef IOT_LOG_LEVEL_GLOBAL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
#else
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
#endif
#endif
#define LIBRARY_LOG_NAME ( "Shadow" )
#include "iot_logging_setup.h"
/*
* Provide default values for undefined memory allocation functions based on
* the usage of dynamic memory allocation.
*/
#if IOT_STATIC_MEMORY_ONLY == 1
#include "iot_static_memory.h"
/**
* @brief Allocate a #_shadowOperation_t. This function should have the same
* signature as [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
void * AwsIotShadow_MallocOperation( size_t size );
/**
* @brief Free a #_shadowOperation_t. This function should have the same
* signature as [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void AwsIotShadow_FreeOperation( void * ptr );
/**
* @brief Allocate a buffer for a short string, used for topic names or client
* tokens. This function should have the same signature as [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
#define AwsIotShadow_MallocString Iot_MallocMessageBuffer
/**
* @brief Free a string. This function should have the same signature as
* [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
#define AwsIotShadow_FreeString Iot_FreeMessageBuffer
/**
* @brief Allocate a #_shadowSubscription_t. This function should have the
* same signature as [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
void * AwsIotShadow_MallocSubscription( size_t size );
/**
* @brief Free a #_shadowSubscription_t. This function should have the same
* signature as [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void AwsIotShadow_FreeSubscription( void * ptr );
#else /* if IOT_STATIC_MEMORY_ONLY == 1 */
#ifndef AwsIotShadow_MallocOperation
#ifdef Iot_DefaultMalloc
#define AwsIotShadow_MallocOperation Iot_DefaultMalloc
#else
#error "No malloc function defined for AwsIotShadow_MallocOperation"
#endif
#endif
#ifndef AwsIotShadow_FreeOperation
#ifdef Iot_DefaultFree
#define AwsIotShadow_FreeOperation Iot_DefaultFree
#else
#error "No free function defined for AwsIotShadow_FreeOperation"
#endif
#endif
#ifndef AwsIotShadow_MallocString
#ifdef Iot_DefaultMalloc
#define AwsIotShadow_MallocString Iot_DefaultMalloc
#else
#error "No malloc function defined for AwsIotShadow_MallocString"
#endif
#endif
#ifndef AwsIotShadow_FreeString
#ifdef Iot_DefaultFree
#define AwsIotShadow_FreeString Iot_DefaultFree
#else
#error "No free function defined for AwsIotShadow_FreeString"
#endif
#endif
#ifndef AwsIotShadow_MallocSubscription
#ifdef Iot_DefaultMalloc
#define AwsIotShadow_MallocSubscription Iot_DefaultMalloc
#else
#error "No malloc function defined for AwsIotShadow_MallocSubscription"
#endif
#endif
#ifndef AwsIotShadow_FreeSubscription
#ifdef Iot_DefaultFree
#define AwsIotShadow_FreeSubscription Iot_DefaultFree
#else
#error "No free function defined for AwsIotShadow_FreeSubscription"
#endif
#endif
#endif /* if IOT_STATIC_MEMORY_ONLY == 1 */
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Provide default values for undefined configuration constants.
*/
#ifndef AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS
#define AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS ( 5000 )
#endif
/** @endcond */
/**
* @brief The number of currently available Shadow operations.
*
* The 3 Shadow operations are DELETE, GET, and UPDATE.
*/
#define SHADOW_OPERATION_COUNT ( 3 )
/**
* @brief The number of currently available Shadow callbacks.
*
* The 2 Shadow callbacks are `update/delta` (AKA "Delta") and `/update/documents`
* (AKA "Updated").
*/
#define SHADOW_CALLBACK_COUNT ( 2 )
/**
* @brief The string representing a Shadow DELETE operation in a Shadow MQTT topic.
*/
#define SHADOW_DELETE_OPERATION_STRING "/shadow/delete"
/**
* @brief The length of #SHADOW_DELETE_OPERATION_STRING.
*/
#define SHADOW_DELETE_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( SHADOW_DELETE_OPERATION_STRING ) - 1 ) )
/**
* @brief The string representing a Shadow GET operation in a Shadow MQTT topic.
*/
#define SHADOW_GET_OPERATION_STRING "/shadow/get"
/**
* @brief The length of #SHADOW_GET_OPERATION_STRING.
*/
#define SHADOW_GET_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( SHADOW_GET_OPERATION_STRING ) - 1 ) )
/**
* @brief The string representing a Shadow UPDATE operation in a Shadow MQTT topic.
*/
#define SHADOW_UPDATE_OPERATION_STRING "/shadow/update"
/**
* @brief The length of #SHADOW_UPDATE_OPERATION_STRING.
*/
#define SHADOW_UPDATE_OPERATION_STRING_LENGTH ( ( uint16_t ) ( sizeof( SHADOW_UPDATE_OPERATION_STRING ) - 1 ) )
/**
* @brief The suffix for a Shadow delta topic.
*/
#define SHADOW_DELTA_SUFFIX "/delta"
/**
* @brief The length of #SHADOW_DELTA_SUFFIX.
*/
#define SHADOW_DELTA_SUFFIX_LENGTH ( ( uint16_t ) ( sizeof( SHADOW_DELTA_SUFFIX ) - 1 ) )
/**
* @brief The suffix for a Shadow updated topic.
*/
#define SHADOW_UPDATED_SUFFIX "/documents"
/**
* @brief The length of #SHADOW_UPDATED_SUFFIX.
*/
#define SHADOW_UPDATED_SUFFIX_LENGTH ( ( uint16_t ) ( sizeof( SHADOW_UPDATED_SUFFIX ) - 1 ) )
/**
* @brief The length of the longest Shadow suffix.
*/
#define SHADOW_LONGEST_SUFFIX_LENGTH SHADOW_UPDATED_SUFFIX_LENGTH
/**
* @brief The macro to convert MQTT error codes to Shadow error codes.
* Below are the conversions happening.
* IOT_MQTT_SUCCESS to AWS_IOT_SHADOW_SUCCESS
* IOT_MQTT_NO_MEMORY to AWS_IOT_SHADOW_NO_MEMORY
* all other error codes to AWS_IOT_SHADOW_MQTT_ERROR
*/
#define SHADOW_CONVERT_STATUS_CODE_MQTT_TO_SHADOW( X ) \
( ( X ) == IOT_MQTT_SUCCESS ) ? AWS_IOT_SHADOW_SUCCESS : \
( ( X ) == IOT_MQTT_NO_MEMORY ) ? AWS_IOT_SHADOW_NO_MEMORY : \
AWS_IOT_SHADOW_MQTT_ERROR
/*----------------------- Shadow internal data types ------------------------*/
/**
* @brief Enumerations representing each of the Shadow library's API functions.
*/
typedef enum _shadowOperationType
{
/* Shadow operations. */
SHADOW_DELETE = 0, /**< @ref shadow_function_deleteasync */
SHADOW_GET = 1, /**< @ref shadow_function_getasync */
SHADOW_UPDATE = 2, /**< @ref shadow_function_updateasync */
/* Shadow callbacks. */
SET_DELTA_CALLBACK = 3, /**< @ref shadow_function_setdeltacallback */
SET_UPDATED_CALLBACK = 4 /**< @ref shadow_function_setupdatedcallback */
} _shadowOperationType_t;
/**
* @brief Enumerations representing each of the Shadow callback functions.
*/
typedef enum _shadowCallbackType
{
DELTA_CALLBACK = 0, /**< Delta callback. */
UPDATED_CALLBACK = 1 /**< Updated callback. */
} _shadowCallbackType_t;
/**
* @brief Represents a Shadow subscriptions object.
*
* These structures are stored in a list.
*/
typedef struct _shadowSubscription
{
IotLink_t link; /**< @brief List link member. */
int32_t references[ SHADOW_OPERATION_COUNT ]; /**< @brief Reference counter for Shadow operation topics. */
AwsIotShadowCallbackInfo_t callbacks[ SHADOW_CALLBACK_COUNT ]; /**< @brief Shadow callbacks for this Thing. */
/**
* @brief Buffer allocated for removing Shadow topics.
*
* This buffer is pre-allocated to ensure that memory is available when
* unsubscribing.
*/
char * pTopicBuffer;
size_t thingNameLength; /**< @brief Length of Thing Name. */
char pThingName[]; /**< @brief Thing Name associated with this subscriptions object. */
} _shadowSubscription_t;
/**
* @brief Internal structure representing a single Shadow operation (DELETE,
* GET, or UPDATE).
*
* A list of these structures keeps track of all in-progress Shadow operations.
*/
typedef struct _shadowOperation
{
IotLink_t link; /**< @brief List link member. */
/* Basic operation information. */
_shadowOperationType_t type; /**< @brief Operation type. */
uint32_t flags; /**< @brief Flags passed to operation API function. */
AwsIotShadowError_t status; /**< @brief Status of operation. */
IotMqttConnection_t mqttConnection; /**< @brief MQTT connection associated with this operation. */
_shadowSubscription_t * pSubscription; /**< @brief Shadow subscriptions object associated with this operation. */
union
{
/* Members valid only for a GET operation. */
struct
{
/**
* @brief Function to allocate memory for an incoming Shadow document.
*
* Only used when the flag #AWS_IOT_SHADOW_FLAG_WAITABLE is set.
*/
void *( *mallocDocument )( size_t );
const char * pDocument; /**< @brief Retrieved Shadow document. */
size_t documentLength; /**< @brief Length of retrieved Shadow document. */
} get;
/* Members valid only for an UPDATE operation. */
struct
{
const char * pClientToken; /**< @brief Client token in update document. */
size_t clientTokenLength; /**< @brief Length of client token. */
} update;
} u; /**< @brief Valid member depends on _shadowOperation_t.type. */
/* How to notify of an operation's completion. */
union
{
IotSemaphore_t waitSemaphore; /**< @brief Semaphore to be used with @ref shadow_function_wait. */
AwsIotShadowCallbackInfo_t callback; /**< @brief User-provided callback function and parameter. */
} notify; /**< @brief How to notify of an operation's completion. */
} _shadowOperation_t;
/* Declarations of names printed in logs. */
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
extern const char * const _pAwsIotShadowOperationNames[];
extern const char * const _pAwsIotShadowCallbackNames[];
#endif
/* Declarations of variables for internal Shadow files. */
extern uint32_t _AwsIotShadowMqttTimeoutMs;
extern IotListDouble_t _AwsIotShadowPendingOperations;
extern IotListDouble_t _AwsIotShadowSubscriptions;
extern IotMutex_t _AwsIotShadowPendingOperationsMutex;
extern IotMutex_t _AwsIotShadowSubscriptionsMutex;
/*----------------------- Shadow operation functions ------------------------*/
/**
* @brief Create a record for a new in-progress Shadow operation.
*
* @param[out] pNewOperation Set to point to the new operation on success.
* @param[in] operation The type of Shadow operation.
* @param[in] flags Flags variables passed to a user-facing Shadow function.
* @param[in] pCallbackInfo User-provided callback function and parameter.
*
* @return #AWS_IOT_SHADOW_SUCCESS or #AWS_IOT_SHADOW_NO_MEMORY
*/
AwsIotShadowError_t _AwsIotShadow_CreateOperation( _shadowOperation_t ** pNewOperation,
_shadowOperationType_t operation,
uint32_t flags,
const AwsIotShadowCallbackInfo_t * pCallbackInfo );
/**
* @brief Free resources used to record a Shadow operation. This is called when
* the operation completes.
*
* @param[in] pData The operation which completed. This parameter is of type
* `void*` to match the signature of [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void _AwsIotShadow_DestroyOperation( void * pData );
/**
* @brief Fill a buffer with a Shadow topic.
*
* @param[in] type One of: DELETE, GET, UPDATE.
* @param[in] pThingName Thing Name to place in the topic.
* @param[in] thingNameLength Length of `pThingName`.
* @param[out] pTopicBuffer Address of the buffer for the Shadow topic. If the
* pointer at this address is `NULL`, this function will allocate a new buffer;
* otherwise, it will use the provided buffer.
* @param[out] pOperationTopicLength Length of the Shadow operation topic (excluding
* any suffix) placed in `pTopicBuffer`.
*
* @warning This function does not check the length of `pTopicBuffer`! Any provided
* buffer must be large enough to accommodate the full Shadow topic, plus
* #SHADOW_LONGEST_SUFFIX_LENGTH.
*
* @return #AWS_IOT_SHADOW_SUCCESS or #AWS_IOT_SHADOW_NO_MEMORY. This function
* will not return #AWS_IOT_SHADOW_NO_MEMORY when a buffer is provided.
*/
AwsIotShadowError_t _AwsIotShadow_GenerateShadowTopic( _shadowOperationType_t type,
const char * pThingName,
size_t thingNameLength,
char ** pTopicBuffer,
uint16_t * pOperationTopicLength );
/**
* @brief Process a Shadow operation by sending the necessary MQTT packets.
*
* @param[in] mqttConnection The MQTT connection to use.
* @param[in] pThingName Thing Name for the Shadow operation.
* @param[in] thingNameLength Length of `pThingName`.
* @param[in] pOperation Operation data to process.
* @param[in] pDocumentInfo Information on the Shadow document for GET or UPDATE
* operations.
*
* @return #AWS_IOT_SHADOW_STATUS_PENDING on success. On error, one of
* #AWS_IOT_SHADOW_NO_MEMORY or #AWS_IOT_SHADOW_MQTT_ERROR.
*/
AwsIotShadowError_t _AwsIotShadow_ProcessOperation( IotMqttConnection_t mqttConnection,
const char * pThingName,
size_t thingNameLength,
_shadowOperation_t * pOperation,
const AwsIotShadowDocumentInfo_t * pDocumentInfo );
/*---------------------- Shadow subscription functions ----------------------*/
/**
* @brief Find a Shadow subscription object. May create a new subscription object
* and adds it to the subscription list if not found.
*
* @param[in] pThingName Thing Name in the subscription object.
* @param[in] thingNameLength Length of `pThingName`.
* @param[in] createIfNotFound If `true`, attempt to create a new subscription
* object if no match is found.
*
* @return Pointer to a Shadow subscription object, either found or newly
* allocated. Returns `NULL` if no subscription object is found and a new
* subscription object could not be allocated.
*
* @note This function should be called with the subscription list mutex locked.
*/
_shadowSubscription_t * _AwsIotShadow_FindSubscription( const char * pThingName,
size_t thingNameLength,
bool createIfNotFound );
/**
* @brief Remove a Shadow subscription object from the subscription list if
* unreferenced.
*
* @param[in] pSubscription Subscription object to check. If this object has no
* active references, it is removed from the subscription list.
* @param[out] pRemovedSubscription Removed subscription object, if any. Optional;
* pass `NULL` to ignore. If not `NULL`, this parameter will be set to the removed
* subscription and that subscription will not be destroyed.
*
* @note This function should be called with the subscription list mutex locked.
*/
void _AwsIotShadow_RemoveSubscription( _shadowSubscription_t * pSubscription,
_shadowSubscription_t ** pRemovedSubscription );
/**
* @brief Free resources used for a Shadow subscription object.
*
* @param[in] pData The subscription object to destroy. This parameter is of type
* `void*` to match the signature of [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
void _AwsIotShadow_DestroySubscription( void * pData );
/**
* @brief Increment the reference count of a Shadow subscriptions object.
*
* Also adds MQTT subscriptions if necessary.
*
* @param[in] pOperation The operation for which the reference count should be
* incremented.
* @param[in] pTopicBuffer Topic buffer containing the operation topic, used if
* subscriptions need to be added.
* @param[in] operationTopicLength The length of the operation topic in `pTopicBuffer`.
* @param[in] callback MQTT callback function for when this operation completes.
*
* @warning This function does not check the length of `pTopicBuffer`! Any provided
* buffer must already contain the Shadow operation topic, plus enough space for the
* status suffix.
*
* @return #AWS_IOT_SHADOW_SUCCESS on success. On error, one of
* #AWS_IOT_SHADOW_NO_MEMORY or #AWS_IOT_SHADOW_MQTT_ERROR.
*
* @note This function should be called with the subscription list mutex locked.
*/
AwsIotShadowError_t _AwsIotShadow_IncrementReferences( _shadowOperation_t * pOperation,
char * pTopicBuffer,
uint16_t operationTopicLength,
AwsIotMqttCallbackFunction_t callback );
/**
* @brief Decrement the reference count of a Shadow subscriptions object.
*
* Also removed MQTT subscriptions and deletes the subscription object if necessary.
*
* @param[in] pOperation The operation for which the reference count should be
* decremented.
* @param[in] pTopicBuffer Topic buffer containing the operation topic, used if
* subscriptions need to be removed.
* @param[out] pRemovedSubscription Set to point to a removed subscription.
* Optional; pass `NULL` to ignore. If not `NULL`, this function will not destroy
* a removed subscription.
*
* @warning This function does not check the length of `pTopicBuffer`! Any provided
* buffer must be large enough to accommodate the full Shadow topic, plus
* #SHADOW_LONGEST_SUFFIX_LENGTH.
*
* @note This function should be called with the subscription list mutex locked.
*/
void _AwsIotShadow_DecrementReferences( _shadowOperation_t * pOperation,
char * pTopicBuffer,
_shadowSubscription_t ** pRemovedSubscription );
/*------------------------- Shadow parser functions -------------------------*/
/**
* @brief Parse a Shadow error document.
*
* @param[in] pErrorDocument The error document to parse.
* @param[in] errorDocumentLength The length of `pErrorDocument`.
*
* @return One of the #AwsIotShadowError_t ranging from 400 to 500 on success.
* #AWS_IOT_SHADOW_BAD_RESPONSE on error.
*/
AwsIotShadowError_t _AwsIotShadow_ParseErrorDocument( const char * pErrorDocument,
size_t errorDocumentLength );
#endif /* ifndef AWS_IOT_SHADOW_INTERNAL_H_ */

View file

@ -0,0 +1,7 @@
+ standard
Contains the implementation of IoT libraries that implement standard protocols
and interfaces, such as MQTT.
+ aws
Contains the implementation of AWS IoT libraries that implement device side
interfaces for using AWS IoT services such as Shadow, Jobs.

View file

@ -0,0 +1,213 @@
/*
* IoT Platform V1.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_clock.h
* @brief Time-related functions used by libraries in this SDK.
*/
#ifndef IOT_CLOCK_H_
#define IOT_CLOCK_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/* Platform layer types include. */
#include "types/iot_platform_types.h"
/**
* @functionspage{platform_clock,platform clock component,Clock}
* - @functionname{platform_clock_function_gettimestring}
* - @functionname{platform_clock_function_gettimems}
* - @functionname{platform_clock_function_sleepms}
* - @functionname{platform_clock_function_timercreate}
* - @functionname{platform_clock_function_timerdestroy}
* - @functionname{platform_clock_function_timerarm}
*/
/**
* @functionpage{IotClock_GetTimestring,platform_clock,gettimestring}
* @functionpage{IotClock_GetTimeMs,platform_clock,gettimems}
* @functionpage{IotClock_SleepMs,platform_clock,sleepms}
* @functionpage{IotClock_TimerCreate,platform_clock,timercreate}
* @functionpage{IotClock_TimerDestroy,platform_clock,timerdestroy}
* @functionpage{IotClock_TimerArm,platform_clock,timerarm}
*/
/**
* @brief Generates a human-readable timestring, such as "01 Jan 2018 12:00".
*
* This function uses the system clock to generate a human-readable timestring.
* This timestring is printed by the [logging functions](@ref logging_functions).
*
* @param[out] pBuffer A buffer to store the timestring in.
* @param[in] bufferSize The size of `pBuffer`.
* @param[out] pTimestringLength The actual length of the timestring stored in
* `pBuffer`.
*
* @return `true` if a timestring was successfully generated; `false` otherwise.
*
* @warning The implementation of this function must not call any [logging functions]
* (@ref logging_functions).
*
* <b>Example</b>
* @code{c}
* char timestring[ 32 ];
* size_t timestringLength = 0;
*
* if( IotClock_GetTimestring( timestring, 32, &timestringLength ) == true )
* {
* printf( "Timestring: %.*s", timestringLength, timestring );
* }
* @endcode
*/
/* @[declare_platform_clock_gettimestring] */
bool IotClock_GetTimestring( char * pBuffer,
size_t bufferSize,
size_t * pTimestringLength );
/* @[declare_platform_clock_gettimestring] */
/**
* @brief Returns a nonzero, monotonically-increasing system time in milliseconds.
*
* This function reads a millisecond-resolution system clock. The clock should
* always be monotonically-increasing; therefore, real-time clocks that may be
* set by another process are not suitable for this function's implementation.
*
* @return The value of the system clock. This function is not expected to fail.
*
* <b>Example</b>
* @code{c}
* // Get current time.
* uint64_t currentTime = IotClock_GetTimeMs();
* @endcode
*/
/* @[declare_platform_clock_gettimems] */
uint64_t IotClock_GetTimeMs( void );
/* @[declare_platform_clock_gettimems] */
/**
* @brief Delay for the given number of milliseconds.
*
* This function suspends its calling thread for at least `sleepTimeMs` milliseconds.
*
* @param[in] sleepTimeMs Sleep time (in milliseconds).
*/
/* @[declare_platform_clock_sleepms] */
void IotClock_SleepMs( uint32_t sleepTimeMs );
/* @[declare_platform_clock_sleepms] */
/**
* @brief Create a new timer.
*
* This function creates a new, unarmed timer. It must be called on an uninitialized
* #IotTimer_t. This function must not be called on an already-initialized #IotTimer_t.
*
* @param[out] pNewTimer Set to a new timer handle on success.
* @param[in] expirationRoutine The function to run when this timer expires. This
* function should be called in its own <i>detached</i> thread.
* @param[in] pArgument The argument to pass to `expirationRoutine`.
*
* @return `true` if the timer is successfully created; `false` otherwise.
*
* @see @ref platform_clock_function_timerdestroy, @ref platform_clock_function_timerarm
*/
/* @[declare_platform_clock_timercreate] */
bool IotClock_TimerCreate( IotTimer_t * pNewTimer,
IotThreadRoutine_t expirationRoutine,
void * pArgument );
/* @[declare_platform_clock_timercreate] */
/**
* @brief Free resources used by a timer.
*
* This function frees resources used by a timer. It must be called on an initialized
* #IotTimer_t. No other timer functions should be called on `pTimer` after calling
* this function (unless the timer is re-created).
*
* This function will stop the `pTimer` if it is armed.
*
* @param[in] pTimer The timer to destroy.
*
* @see @ref platform_clock_function_timercreate, @ref platform_clock_function_timerarm
*/
/* @[declare_platform_clock_timerdestroy] */
void IotClock_TimerDestroy( IotTimer_t * pTimer );
/* @[declare_platform_clock_timerdestroy] */
/**
* @brief Arm a timer to expire at the given relative timeout.
*
* This function arms a timer to run its expiration routine at the given time.
*
* If `periodMs` is nonzero, the timer should expire periodically at intervals
* such as:
* - `relativeTimeoutMs`
* - `relativeTimeoutMs + periodMs`
* - `relativeTimeoutMs + 2 * periodMs`
* - Etc. (subject to some jitter).
*
* Setting `periodMs` to `0` arms a one-shot, non-periodic timer.
*
* @param[in] pTimer The timer to arm.
* @param[in] relativeTimeoutMs When the timer should expire, relative to the time
* this function is called.
* @param[in] periodMs How often the timer should expire again after `relativeTimerMs`.
*
* @return `true` if the timer was successfully armed; `false` otherwise.
*
* @see @ref platform_clock_function_timercreate, @ref platform_clock_function_timerdestroy
*
* <b>Example</b>
* @code{c}
*
* void timerExpirationRoutine( void * pArgument );
*
* void timerExample( void )
* {
* IotTimer_t timer;
*
* if( IotClock_TimerCreate( &timer, timerExpirationRoutine, NULL ) == true )
* {
* // Set the timer to periodically expire every 10 seconds.
* if( IotClock_TimerArm( &timer, 10000, 10000 ) == true )
* {
* // Wait for timer to expire.
* }
*
* IotClock_TimerDestroy( &timer );
* }
* }
* @endcode
*/
/* @[declare_platform_clock_timerarm] */
bool IotClock_TimerArm( IotTimer_t * pTimer,
uint32_t relativeTimeoutMs,
uint32_t periodMs );
/* @[declare_platform_clock_timerarm] */
#endif /* ifndef IOT_CLOCK_H_ */

View file

@ -0,0 +1,100 @@
/*
* IoT Platform V1.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_metrics.h
* @brief Functions for retrieving [Device Defender](@ref defender) metrics.
*
* The functions in this header are only required by Device Defender. They do not
* need to be implemented if Device Defender is not used.
*/
#ifndef IOT_METRICS_H_
#define IOT_METRICS_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
/* Linear containers (lists and queues) include. */
#include "iot_linear_containers.h"
/**
* @functionspage{platform_metrics,platform metrics component,Metrics}
* - @functionname{platform_metrics_function_init}
* - @functionname{platform_metrics_function_cleanup}
* - @functionname{platform_metrics_function_gettcpconnections}
*/
/**
* @functionpage{IotMetrics_Init,platform_metrics,init}
* @functionpage{IotMetrics_Cleanup,platform_metrics,cleanup}
* @functionpage{IotMetrics_GetTcpConnections,platform_metrics,gettcpconnections}
*/
/**
* @brief One-time initialization function for the platform metrics component.
*
* This function initializes the platform metrics component. <b>It must be called
* once (and only once) before calling any other metrics or [Device Defender function]
* (@ref defender_functions).</b> Calling this function more than once without first
* calling @ref platform_metrics_function_cleanup may result in a crash.
*
* @return `true` is initialization succeeded; `false` otherwise.
*
* @warning No thread-safety guarantees are provided for this function.
*/
/* @[declare_platform_metrics_init] */
bool IotMetrics_Init( void );
/* @[declare_platform_metrics_init] */
/**
* @brief One-time deinitialization function for the platform metrics component.
*
* This function frees resources taken in @ref platform_metrics_function_init.
* No other metrics or [Device Defender functions](@ref defender_functions) may
* be called unless @ref platform_metrics_function_init is called again.
*
* @warning No thread-safety guarantees are provided for this function.
*/
/* @[declare_platform_metrics_cleanup] */
void IotMetrics_Cleanup( void );
/* @[declare_platform_metrics_cleanup] */
/**
* @brief Retrieve a list of active TCP connections from the system.
*
* The provided connections are reported by Device Defender.
*
* @param[in] pContext Context passed as the first parameter of `metricsCallback`.
* @param[in] metricsCallback Called by this function to provide the list of TCP
* connections. The list given by this function is should not be used after the
* callback returns.
*/
/* @[declare_platform_metrics_gettcpconnections] */
void IotMetrics_GetTcpConnections( void * pContext,
void ( * metricsCallback )( void *, const IotListDouble_t * ) );
/* @[declare_platform_metrics_gettcpconnections] */
#endif /* ifndef IOT_METRICS_H_ */

View file

@ -0,0 +1,318 @@
/*
* IoT Platform V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_network.h
* @brief Abstraction of network functions used by libraries in this SDK.
*/
#ifndef IOT_NETWORK_H_
#define IOT_NETWORK_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdint.h>
#include <stdlib.h>
/**
* @ingroup platform_datatypes_enums
* @brief Return codes for [network functions](@ref platform_network_functions).
*/
typedef enum IotNetworkError
{
IOT_NETWORK_SUCCESS = 0, /**< Function successfully completed. */
IOT_NETWORK_FAILURE, /**< Generic failure not covered by other values. */
IOT_NETWORK_BAD_PARAMETER, /**< At least one parameter was invalid. */
IOT_NETWORK_NO_MEMORY, /**< Memory allocation failed. */
IOT_NETWORK_SYSTEM_ERROR /**< An error occurred when calling a system API. */
} IotNetworkError_t;
/**
* @page platform_network_functions Networking
* @brief Functions of the network abstraction component.
*
* The network abstraction component does not declare functions, but uses function
* pointers instead. This allows multiple network stacks to be used at the same time.
* Libraries that require the network will request an #IotNetworkInterface_t
* parameter. The members of the #IotNetworkInterface_t will be called whenever
* the library interacts with the network.
*
* The following function pointers are associated with an #IotNetworkInterface_t.
* Together, they represent a network stack.
* - @functionname{platform_network_function_create}
* - @functionname{platform_network_function_setreceivecallback}
* - @functionname{platform_network_function_send}
* - @functionname{platform_network_function_receive}
* - @functionname{platform_network_function_close}
* - @functionname{platform_network_function_destroy}
* - @functionname{platform_network_function_receivecallback}
*/
/**
* @functionpage{IotNetworkInterface_t::create,platform_network,create}
* @functionpage{IotNetworkInterface_t::setReceiveCallback,platform_network,setreceivecallback}
* @functionpage{IotNetworkInterface_t::send,platform_network,send}
* @functionpage{IotNetworkInterface_t::receive,platform_network,receive}
* @functionpage{IotNetworkInterface_t::close,platform_network,close}
* @functionpage{IotNetworkInterface_t::destroy,platform_network,destroy}
* @functionpage{IotNetworkReceiveCallback_t,platform_network,receivecallback}
*/
/**
* @brief Provide an asynchronous notification of incoming network data.
*
* A function with this signature may be set with @ref platform_network_function_setreceivecallback
* to be invoked when data is available on the network.
*
* @param[in] pConnection The connection on which data is available, defined by
* the network stack.
* @param[in] pContext The third argument passed to @ref platform_network_function_setreceivecallback.
*/
/* @[declare_platform_network_receivecallback] */
typedef void ( * IotNetworkReceiveCallback_t )( IotNetworkConnection_t pConnection,
void * pContext );
/* @[declare_platform_network_receivecallback] */
/**
* @ingroup platform_datatypes_paramstructs
* @brief Represents the functions of a network stack.
*
* Functions that match these signatures should be implemented against a system's
* network stack. See the `platform` directory for existing implementations.
*/
typedef struct IotNetworkInterface
{
/**
* @brief Create a new network connection.
*
* This function allocates resources and establishes a new network connection.
* @param[in] pServerInfo Represents information needed to set up the
* new connection, defined by the network stack.
* @param[in] pCredentialInfo Represents information needed to secure the
* new connection, defined by the network stack.
* @param[out] pConnection Set to represent a new connection, defined by the
* network stack.
*
* @return Any #IotNetworkError_t, as defined by the network stack.
*/
/* @[declare_platform_network_create] */
IotNetworkError_t ( * create )( IotNetworkServerInfo_t pServerInfo,
IotNetworkCredentials_t pCredentialInfo,
IotNetworkConnection_t * pConnection );
/* @[declare_platform_network_create] */
/**
* @brief Register an @ref platform_network_function_receivecallback.
*
* Sets an @ref platform_network_function_receivecallback to be called
* asynchronously when data arrives on the network. The network stack
* should invoke this function "as if" it were the thread routine of a
* detached thread.
*
* Each network connection may only have one receive callback at any time.
* @ref platform_network_function_close is expected to remove any active
* receive callbacks.
*
* @param[in] pConnection The connection to associate with the receive callback.
* @param[in] receiveCallback The function to invoke for incoming network data.
* @param[in] pContext A value to pass as the first parameter to the receive callback.
*
* @return Any #IotNetworkError_t, as defined by the network stack.
*
* @see platform_network_function_receivecallback
*/
/* @[declare_platform_network_setreceivecallback] */
IotNetworkError_t ( * setReceiveCallback )( IotNetworkConnection_t pConnection,
IotNetworkReceiveCallback_t receiveCallback,
void * pContext );
/* @[declare_platform_network_setreceivecallback] */
/**
* @brief Send data over a return connection.
*
* Attempts to transmit `messageLength` bytes of `pMessage` across the
* connection represented by `pConnection`. Returns the number of bytes
* actually sent, `0` on failure.
*
* @param[in] pConnection The connection used to send data, defined by the
* network stack.
* @param[in] pMessage The message to send.
* @param[in] messageLength The length of `pMessage`.
*
* @return The number of bytes successfully sent, `0` on failure.
*/
/* @[declare_platform_network_send] */
size_t ( * send )( IotNetworkConnection_t pConnection,
const uint8_t * pMessage,
size_t messageLength );
/* @[declare_platform_network_send] */
/**
* @brief Block and wait for incoming network data.
*
* Wait for a message of size `bytesRequested` to arrive on the network and
* place it in `pBuffer`.
*
* @param[in] pConnection The connection to wait on, defined by the network
* stack.
* @param[out] pBuffer Where to place the incoming network data. This buffer
* must be at least `bytesRequested` in size.
* @param[in] bytesRequested How many bytes to wait for. `pBuffer` must be at
* least this size.
*
* @return The number of bytes successfully received. This should be
* `bytesRequested` when successful. Any other value may indicate an error.
*/
/* @[declare_platform_network_receive] */
size_t ( * receive )( IotNetworkConnection_t pConnection,
uint8_t * pBuffer,
size_t bytesRequested );
/* @[declare_platform_network_receive] */
/**
* @brief Read incoming data available in the network buffers.
*
* Reads bytes available in the network buffers into `pBuffer`.
* - If there is less data available than requested, it will return
* the available number of bytes.
* - If there is more data available than requested, it will fill the
* whole `pBuffer`.
* - If there is no data available, it will return 0.
*
* @param[in] pConnection The connection to receive data on, defined by
* the network stack.
* @param[out] pBuffer The buffer to place the incoming network data.
* @param[in] bufferSize The size of `pBuffer`.
*
* @return The number of bytes successfully received.
*/
/* @[declare_platform_network_receiveupto] */
size_t ( * receiveUpto )( IotNetworkConnection_t pConnection,
uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_platform_network_receiveupto] */
/**
* @brief Close a network connection.
*
* This function closes the connection, but does not release the resources
* used by the connection. This allows calls to other networking functions
* to return an error and handle a closed connection without the risk of
* crashing. Once it can be guaranteed that `pConnection` will no longer be
* used, the connection can be destroyed with @ref platform_network_function_destroy.
*
* In addition to closing the connection, this function should also remove
* any active [receive callback](@ref platform_network_function_receivecallback).
*
* @param[in] pConnection The network connection to close, defined by the
* network stack.
*
* @return Any #IotNetworkError_t, as defined by the network stack.
*
* @note It must be safe to call this function on an already-closed connection.
*/
/* @[declare_platform_network_close] */
IotNetworkError_t ( * close )( IotNetworkConnection_t pConnection );
/* @[declare_platform_network_close] */
/**
* @brief Free resources used by a network connection.
*
* This function releases the resources of a closed connection. It should be
* called after @ref platform_network_function_close.
*
* @param[in] pConnection The network connection to destroy, defined by
* the network stack.
*
* @return Any #IotNetworkError_t, as defined by the network stack.
*
* @attention No function should be called on the network connection after
* calling this function. This function must be safe to call from a
* [receive callback](@ref platform_network_function_receivecallback).
*/
/* @[declare_platform_network_destroy] */
IotNetworkError_t ( * destroy )( IotNetworkConnection_t pConnection );
/* @[declare_platform_network_destroy] */
} IotNetworkInterface_t;
/**
* @ingroup platform_datatypes_paramstructs
* @brief Information on the remote server for connection setup.
*
* May be passed to #IotNetworkInterface_t.create as `pConnectionInfo`. This
* structure contains commonly-used parameters, but may be replaced with an
* alternative.
*/
struct IotNetworkServerInfo
{
const char * pHostName; /**< @brief Server host name. Must be NULL-terminated. */
uint16_t port; /**< @brief Server port in host-order. */
};
/**
* @ingroup platform_datatypes_paramstructs
* @brief Contains the credentials necessary for connection setup.
*
* May be passed to #IotNetworkInterface_t.create as `pCredentialInfo`. This
* structure contains commonly-used parameters, but may be replaced with an
* alternative.
*/
struct IotNetworkCredentials
{
/**
* @brief Set this to a non-NULL value to use ALPN.
*
* This string must be NULL-terminated.
*
* See [this link]
* (https://aws.amazon.com/blogs/iot/mqtt-with-tls-client-authentication-on-port-443-why-it-is-useful-and-how-it-works/)
* for more information.
*/
const char * pAlpnProtos;
/**
* @brief Set this to a non-zero value to use TLS max fragment length
* negotiation (TLS MFLN).
*
* @note The network stack may have a minimum value for this parameter and
* may return an error if this parameter is too small.
*/
size_t maxFragmentLength;
/**
* @brief Disable server name indication (SNI) for a TLS session.
*/
bool disableSni;
const char * pRootCa; /**< @brief String representing a trusted server root certificate. */
size_t rootCaSize; /**< @brief Size associated with #IotNetworkCredentials.pRootCa. */
const char * pClientCert; /**< @brief String representing the client certificate. */
size_t clientCertSize; /**< @brief Size associated with #IotNetworkCredentials.pClientCert. */
const char * pPrivateKey; /**< @brief String representing the client certificate's private key. */
size_t privateKeySize; /**< @brief Size associated with #IotNetworkCredentials.pPrivateKey. */
};
#endif /* ifndef IOT_NETWORK_H_ */

View file

@ -0,0 +1,352 @@
/*
* IoT Platform V1.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_threads.h
* @brief Threading and synchronization functions used by libraries in this SDK.
*/
#ifndef IOT_THREADS_H_
#define IOT_THREADS_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <stdint.h>
/* Platform layer types include. */
#include "types/iot_platform_types.h"
/**
* @functionspage{platform_threads,platform thread management,Thread Management}
* - @functionname{platform_threads_function_createdetachedthread}
* - @functionname{platform_threads_function_mutexcreate}
* - @functionname{platform_threads_function_mutexdestroy}
* - @functionname{platform_threads_function_mutexlock}
* - @functionname{platform_threads_function_mutextrylock}
* - @functionname{platform_threads_function_mutexunlock}
* - @functionname{platform_threads_function_semaphorecreate}
* - @functionname{platform_threads_function_semaphoredestroy}
* - @functionname{platform_threads_function_semaphoregetcount}
* - @functionname{platform_threads_function_semaphorewait}
* - @functionname{platform_threads_function_semaphoretrywait}
* - @functionname{platform_threads_function_semaphoretimedwait}
* - @functionname{platform_threads_function_semaphorepost}
*/
/**
* @functionpage{Iot_CreateDetachedThread,platform_threads,createdetachedthread}
* @functionpage{IotMutex_Create,platform_threads,mutexcreate}
* @functionpage{IotMutex_Destroy,platform_threads,mutexdestroy}
* @functionpage{IotMutex_Lock,platform_threads,mutexlock}
* @functionpage{IotMutex_TryLock,platform_threads,mutextrylock}
* @functionpage{IotMutex_Unlock,platform_threads,mutexunlock}
* @functionpage{IotSemaphore_Create,platform_threads,semaphorecreate}
* @functionpage{IotSemaphore_Destroy,platform_threads,semaphoredestroy}
* @functionpage{IotSemaphore_GetCount,platform_threads,semaphoregetcount}
* @functionpage{IotSemaphore_Wait,platform_threads,semaphorewait}
* @functionpage{IotSemaphore_TryWait,platform_threads,semaphoretrywait}
* @functionpage{IotSemaphore_TimedWait,platform_threads,semaphoretimedwait}
* @functionpage{IotSemaphore_Post,platform_threads,semaphorepost}
*/
/**
* @brief Create a new detached thread, i.e. a thread that cleans up after itself.
*
* This function creates a new thread. Threads created by this function exit
* upon returning from the thread routine. Any resources taken must be freed
* by the exiting thread.
*
* @param[in] threadRoutine The function this thread should run.
* @param[in] pArgument The argument passed to `threadRoutine`.
* @param[in] priority Represents the priority of the new thread, as defined by
* the system. The value #IOT_THREAD_DEFAULT_PRIORITY (i.e. `0`) must be used to
* represent the system default for thread priority.
* @param[in] stackSize Represents the stack size of the new thread, as defined
* by the system. The value #IOT_THREAD_DEFAULT_STACK_SIZE (i.e. `0`) must be used
* to represent the system default for stack size.
*
* @return `true` if the new thread was successfully created; `false` otherwise.
*
* @code{c}
* // Thread routine.
* void threadRoutine( void * pArgument );
*
* // Run threadRoutine in a detached thread, using default priority and stack size.
* if( Iot_CreateDetachedThread( threadRoutine,
* NULL,
* IOT_THREAD_DEFAULT_PRIORITY,
* IOT_THREAD_DEFAULT_STACK_SIZE ) == true )
* {
* // Success
* }
* else
* {
* // Failure, no thread was created.
* }
* @endcode
*/
/* @[declare_platform_threads_createdetachedthread] */
bool Iot_CreateDetachedThread( IotThreadRoutine_t threadRoutine,
void * pArgument,
int32_t priority,
size_t stackSize );
/* @[declare_platform_threads_createdetachedthread] */
/**
* @brief Create a new mutex.
*
* This function creates a new, unlocked mutex. It must be called on an uninitialized
* #IotMutex_t. This function must not be called on an already-initialized #IotMutex_t.
*
* @param[in] pNewMutex Pointer to the memory that will hold the new mutex.
* @param[in] recursive Set to `true` to create a recursive mutex, i.e. a mutex that
* may be locked multiple times by the same thread. If the system does not support
* recursive mutexes, this function should do nothing and return `false`.
*
* @return `true` if mutex creation succeeds; `false` otherwise.
*
* @see @ref platform_threads_function_mutexdestroy
*
* <b>Example</b>
* @code{c}
* IotMutex_t mutex;
*
* // Create non-recursive mutex.
* if( IotMutex_Create( &mutex, false ) == true )
* {
* // Lock and unlock the mutex...
*
* // Destroy the mutex when it's no longer needed.
* IotMutex_Destroy( &mutex );
* }
* @endcode
*/
/* @[declare_platform_threads_mutexcreate] */
bool IotMutex_Create( IotMutex_t * pNewMutex, bool recursive );
/* @[declare_platform_threads_mutexcreate] */
/**
* @brief Free resources used by a mutex.
*
* This function frees resources used by a mutex. It must be called on an initialized
* #IotMutex_t. No other mutex functions should be called on `pMutex` after calling
* this function (unless the mutex is re-created).
*
* @param[in] pMutex The mutex to destroy.
*
* @warning This function must not be called on a locked mutex.
* @see @ref platform_threads_function_mutexcreate
*/
/* @[declare_platform_threads_mutexdestroy] */
void IotMutex_Destroy( IotMutex_t * pMutex );
/* @[declare_platform_threads_mutexdestroy] */
/**
* @brief Lock a mutex. This function should only return when the mutex is locked;
* it is not expected to fail.
*
* This function blocks and waits until a mutex is available. It waits forever
* (deadlocks) if `pMutex` is already locked and never unlocked.
*
* @param[in] pMutex The mutex to lock.
*
* @see @ref platform_threads_function_mutextrylock for a nonblocking lock.
*/
/* @[declare_platform_threads_mutexlock] */
void IotMutex_Lock( IotMutex_t * pMutex );
/* @[declare_platform_threads_mutexlock] */
/**
* @brief Attempt to lock a mutex. Return immediately if the mutex is not available.
*
* If `pMutex` is available, this function immediately locks it and returns.
* Otherwise, this function returns without locking `pMutex`.
*
* @param[in] pMutex The mutex to lock.
*
* @return `true` if the mutex was successfully locked; `false` if the mutex was
* not available.
*
* @see @ref platform_threads_function_mutexlock for a blocking lock.
*/
/* @[declare_platform_threads_mutextrylock] */
bool IotMutex_TryLock( IotMutex_t * pMutex );
/* @[declare_platform_threads_mutextrylock] */
/**
* @brief Unlock a mutex. This function should only return when the mutex is unlocked;
* it is not expected to fail.
*
* Unlocks a locked mutex. `pMutex` must have been locked by the thread calling
* this function.
*
* @param[in] pMutex The mutex to unlock.
*
* @note This function should not be called on a mutex that is already unlocked.
*/
/* @[declare_platform_threads_mutexunlock] */
void IotMutex_Unlock( IotMutex_t * pMutex );
/* @[declare_platform_threads_mutexunlock] */
/**
* @brief Create a new counting semaphore.
*
* This function creates a new counting semaphore with a given initial and
* maximum value. It must be called on an uninitialized #IotSemaphore_t.
* This function must not be called on an already-initialized #IotSemaphore_t.
*
* @param[in] pNewSemaphore Pointer to the memory that will hold the new semaphore.
* @param[in] initialValue The semaphore should be initialized with this value.
* @param[in] maxValue The maximum value the semaphore will reach.
*
* @return `true` if semaphore creation succeeds; `false` otherwise.
*
* @see @ref platform_threads_function_semaphoredestroy
*
* <b>Example</b>
* @code{c}
* IotSemaphore_t sem;
*
* // Create a locked binary semaphore.
* if( IotSemaphore_Create( &sem, 0, 1 ) == true )
* {
* // Unlock the semaphore.
* IotSemaphore_Post( &sem );
*
* // Destroy the semaphore when it's no longer needed.
* IotSemaphore_Destroy( &sem );
* }
* @endcode
*/
/* @[declare_platform_threads_semaphorecreate] */
bool IotSemaphore_Create( IotSemaphore_t * pNewSemaphore,
uint32_t initialValue,
uint32_t maxValue );
/* @[declare_platform_threads_semaphorecreate] */
/**
* @brief Free resources used by a semaphore.
*
* This function frees resources used by a semaphore. It must be called on an initialized
* #IotSemaphore_t. No other semaphore functions should be called on `pSemaphore` after
* calling this function (unless the semaphore is re-created).
*
* @param[in] pSemaphore The semaphore to destroy.
*
* @warning This function must not be called on a semaphore with waiting threads.
* @see @ref platform_threads_function_semaphorecreate
*/
/* @[declare_platform_threads_semaphoredestroy] */
void IotSemaphore_Destroy( IotSemaphore_t * pSemaphore );
/* @[declare_platform_threads_semaphoredestroy] */
/**
* @brief Query the current count of the semaphore.
*
* This function queries a counting semaphore for its current value. A counting
* semaphore's value is always 0 or positive.
*
* @param[in] pSemaphore The semaphore to query.
*
* @return The current count of the semaphore. This function should not fail.
*/
/* @[declare_platform_threads_semaphoregetcount] */
uint32_t IotSemaphore_GetCount( IotSemaphore_t * pSemaphore );
/* @[declare_platform_threads_semaphoregetcount] */
/**
* @brief Wait on (lock) a semaphore. This function should only return when the
* semaphore wait succeeds; it is not expected to fail.
*
* This function blocks and waits until a counting semaphore is positive. It
* waits forever (deadlocks) if `pSemaphore` has a count `0` that is never
* [incremented](@ref platform_threads_function_semaphorepost).
*
* @param[in] pSemaphore The semaphore to lock.
*
* @see @ref platform_threads_function_semaphoretrywait for a nonblocking wait;
* @ref platform_threads_function_semaphoretimedwait for a wait with timeout.
*/
/* @[declare_platform_threads_semaphorewait] */
void IotSemaphore_Wait( IotSemaphore_t * pSemaphore );
/* @[declare_platform_threads_semaphorewait] */
/**
* @brief Attempt to wait on (lock) a semaphore. Return immediately if the semaphore
* is not available.
*
* If the count of `pSemaphore` is positive, this function immediately decrements
* the semaphore and returns. Otherwise, this function returns without decrementing
* `pSemaphore`.
*
* @param[in] pSemaphore The semaphore to lock.
*
* @return `true` if the semaphore wait succeeded; `false` if the semaphore has
* a count of `0`.
*
* @see @ref platform_threads_function_semaphorewait for a blocking wait;
* @ref platform_threads_function_semaphoretimedwait for a wait with timeout.
*/
/* @[declare_platform_threads_semaphoretrywait] */
bool IotSemaphore_TryWait( IotSemaphore_t * pSemaphore );
/* @[declare_platform_threads_semaphoretrywait] */
/**
* @brief Attempt to wait on (lock) a semaphore with a timeout.
*
* This function blocks and waits until a counting semaphore is positive
* <i>or</i> its timeout expires (whichever is sooner). It decrements
* `pSemaphore` and returns `true` if the semaphore is positive at some
* time during the wait. If `pSemaphore` is always `0` during the wait,
* this function returns `false`.
*
* @param[in] pSemaphore The semaphore to lock.
* @param[in] timeoutMs Relative timeout of semaphore lock. This function returns
* false if the semaphore couldn't be locked within this timeout.
*
* @return `true` if the semaphore wait succeeded; `false` if it timed out.
*
* @see @ref platform_threads_function_semaphoretrywait for a nonblocking wait;
* @ref platform_threads_function_semaphorewait for a blocking wait.
*/
/* @[declare_platform_threads_semaphoretimedwait] */
bool IotSemaphore_TimedWait( IotSemaphore_t * pSemaphore,
uint32_t timeoutMs );
/* @[declare_platform_threads_semaphoretimedwait] */
/**
* @brief Post to (unlock) a semaphore. This function should only return when the
* semaphore post succeeds; it is not expected to fail.
*
* This function increments the count of a semaphore. Any thread may call this
* function to increment a semaphore's count.
*
* @param[in] pSemaphore The semaphore to unlock.
*/
/* @[declare_platform_threads_semaphorepost] */
void IotSemaphore_Post( IotSemaphore_t * pSemaphore );
/* @[declare_platform_threads_semaphorepost] */
#endif /* ifndef IOT_THREADS_H_ */

View file

@ -0,0 +1,190 @@
/*
* IoT Platform V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_platform_types.h
* @brief Types of the platform layer.
*/
#ifndef IOT_PLATFORM_TYPES_H_
#define IOT_PLATFORM_TYPES_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Linear containers (lists and queues) include for metrics types. */
#include "iot_linear_containers.h"
/*------------------------- Thread management types -------------------------*/
/**
* @brief A value representing the system default for new thread priority.
*/
#ifndef IOT_THREAD_DEFAULT_PRIORITY
#define IOT_THREAD_DEFAULT_PRIORITY 0
#endif
/**
* @brief A value representing the system default for new thread stack size.
*/
#ifndef IOT_THREAD_DEFAULT_STACK_SIZE
#define IOT_THREAD_DEFAULT_STACK_SIZE 0
#endif
/**
* @ingroup platform_datatypes_handles
* @brief The type used to represent mutexes, configured with the type
* `_IotSystemMutex_t`.
*
* For the provided ports, `_IotSystemMutex_t` will be automatically configured.
* For other ports, `_IotSystemMutex_t` should be set in `iot_config.h`.
*
* Mutexes should only be released by the threads that take them.
*
* <b>Example</b> <br>
* To change the type of #IotMutex_t to `long`:
* @code{c}
* typedef long _IotSystemMutex_t;
* #include "iot_threads.h"
* @endcode
*/
typedef _IotSystemMutex_t IotMutex_t;
/**
* @ingroup platform_datatypes_handles
* @brief The type used to represent semaphores, configured with the type
* `_IotSystemSemaphore_t`.
*
* For the provided ports, `_IotSystemSemaphore_t` will be automatically configured.
* For other ports, `_IotSystemSemaphore_t` should be set in `iot_config.h`.
*
* Semaphores must be counting, and any thread may take (wait on) or release
* (post to) a semaphore.
*
* <b>Example</b> <br>
* To change the type of #IotSemaphore_t to `long`:
* @code{c}
* typedef long _IotSystemSemaphore_t;
* #include "iot_threads.h"
* @endcode
*/
typedef _IotSystemSemaphore_t IotSemaphore_t;
/**
* @brief Thread routine function.
*
* @param[in] void * The argument passed to the @ref
* platform_threads_function_createdetachedthread. For application use.
*/
typedef void ( * IotThreadRoutine_t )( void * );
/*-------------------------- Clock and timer types --------------------------*/
/**
* @ingroup platform_datatypes_handles
* @brief The type used to represent timers, configured with the type
* `_IotSystemTimer_t`.
*
* For the provided ports, `_IotSystemTimer_t` will be automatically configured.
* For other ports, `_IotSystemTimer_t` should be set in `iot_config.h`.
*
* <b>Example</b> <br>
* To change the type of #IotTimer_t to `long`:
* @code{c}
* typedef long _IotSystemTimer_t;
* #include "iot_clock.h"
* @endcode
*/
typedef _IotSystemTimer_t IotTimer_t;
/*--------------------------- Network stack types ---------------------------*/
/**
* @ingroup platform_datatypes_handles
* @brief The type used to represent network server info, configured with the
* type `_IotNetworkServerInfo_t`.
*
* For the provided ports, `_IotNetworkServerInfo_t` will be automatically configured.
* For other ports, `_IotNetworkServerInfo_t` should be set in `iot_config.h`.
*
* All of the provided ports configure this to #IotNetworkServerInfo, which provides
* the necessary information to connect to a TCP peer. For other network protocols,
* this type should be set to an alternate structure as needed by the other protocol.
*/
typedef _IotNetworkServerInfo_t IotNetworkServerInfo_t;
/**
* @ingroup platform_datatypes_handles
* @brief The type used to represent network credentials, configured with the
* type `_IotNetworkCredentials_t`.
*
* For the provided ports, `_IotNetworkCredentials_t` will be automatically configured.
* For other ports, `_IotNetworkCredentials_t` should be set in `iot_config.h`.
*
* All of the provided ports configure this to #IotNetworkCredentials, which provides
* the necessary information to connect to a TLS server over TCP. For other network
* protocols, this type should be set to an alternate structure as needed by the other
* protocol.
*/
typedef _IotNetworkCredentials_t IotNetworkCredentials_t;
/**
* @ingroup platform_datatypes_handles
* @brief The type used to represent network connections, configured with the
* type `_IotNetworkConnection_t`.
*
* For the provided ports, `_IotNetworkConnection_t` will be automatically configured.
* For other ports, `_IotNetworkConnection_t` should be set in `iot_config.h`.
*/
typedef _IotNetworkConnection_t IotNetworkConnection_t;
/*------------------------------ Metrics types ------------------------------*/
/**
* @brief The length of the buffer used to store IP addresses for metrics.
*
* This is the length of the longest IPv6 address plus space for the port number
* and NULL terminator.
*/
#define IOT_METRICS_IP_ADDRESS_LENGTH 54
/**
* @brief Represents a TCP connection to a remote IPv4 server.
*
* A list of these is provided by @ref platform_metrics_function_gettcpconnections.
*/
typedef struct IotMetricsTcpConnection
{
IotLink_t link; /**< @brief List link member. */
void * pNetworkContext; /**< @brief Context that may be used by metrics or Defender. */
size_t addressLength; /**< @brief The length of the address stored in #IotMetricsTcpConnection_t.pRemoteAddress. */
/**
* @brief NULL-terminated IP address and port in text format.
*
* IPv4 addresses will be in the format `xxx.xxx.xxx.xxx:port`.
* IPv6 addresses will be in the format `[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]:port`.
*/
char pRemoteAddress[ IOT_METRICS_IP_ADDRESS_LENGTH ];
} IotMetricsTcpConnection_t;
#endif /* ifndef IOT_PLATFORM_TYPES_H_ */

View file

@ -0,0 +1,547 @@
/*
* FreeRTOS Kernel V10.2.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* @file atomic.h
* @brief FreeRTOS atomic operation support.
*
* Two implementations of atomic are given in this header file:
* 1. Disabling interrupt globally.
* 2. ISA native atomic support.
* The former is available to all ports (compiler-architecture combination),
* while the latter is only available to ports compiling with GCC (version at
* least 4.7.0), which also have ISA atomic support.
*
* User can select which implementation to use by:
* setting/clearing configUSE_ATOMIC_INSTRUCTION in FreeRTOSConfig.h.
* Define AND set configUSE_ATOMIC_INSTRUCTION to 1 for ISA native atomic support.
* Undefine OR clear configUSE_ATOMIC_INSTRUCTION for disabling global interrupt
* implementation.
*
* @see GCC Built-in Functions for Memory Model Aware Atomic Operations
* https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
*/
#ifndef ATOMIC_H
#define ATOMIC_H
#ifndef INC_FREERTOS_H
#error "include FreeRTOS.h must appear in source files before include atomic.h"
#endif
/* Standard includes. */
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
/* Needed for __atomic_compare_exchange() weak=false. */
#include <stdbool.h>
/* This branch is for GCC compiler and GCC compiler only. */
#ifndef portFORCE_INLINE
#define portFORCE_INLINE inline __attribute__((always_inline))
#endif
#else
/* Port specific definitions -- entering/exiting critical section.
* Refer template -- ./lib/FreeRTOS/portable/Compiler/Arch/portmacro.h
*
* Every call to ATOMIC_EXIT_CRITICAL() must be closely paired with
* ATOMIC_ENTER_CRITICAL().
*/
#if defined( portSET_INTERRUPT_MASK_FROM_ISR )
/* Nested interrupt scheme is supported in this port. */
#define ATOMIC_ENTER_CRITICAL() \
UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR()
#define ATOMIC_EXIT_CRITICAL() \
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType )
#else
/* Nested interrupt scheme is NOT supported in this port. */
#define ATOMIC_ENTER_CRITICAL() portENTER_CRITICAL()
#define ATOMIC_EXIT_CRITICAL() portEXIT_CRITICAL()
#endif /* portSET_INTERRUPT_MASK_FROM_ISR() */
/* Port specific definition -- "always inline".
* Inline is compiler specific, and may not always get inlined depending on your optimization level.
* Also, inline is considered as performance optimization for atomic.
* Thus, if portFORCE_INLINE is not provided by portmacro.h, instead of resulting error,
* simply define it.
*/
#ifndef portFORCE_INLINE
#define portFORCE_INLINE
#endif
#endif /* configUSE_GCC_BUILTIN_ATOMICS */
#define ATOMIC_COMPARE_AND_SWAP_SUCCESS 0x1U /**< Compare and swap succeeded, swapped. */
#define ATOMIC_COMPARE_AND_SWAP_FAILURE 0x0U /**< Compare and swap failed, did not swap. */
/*----------------------------- Swap && CAS ------------------------------*/
/**
* Atomic compare-and-swap
*
* @brief Performs an atomic compare-and-swap operation on the specified values.
*
* @param[in, out] pDestination Pointer to memory location from where value is
* to be loaded and checked.
* @param[in] ulExchange If condition meets, write this value to memory.
* @param[in] ulComparand Swap condition.
*
* @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
*
* @note This function only swaps *pDestination with ulExchange, if previous
* *pDestination value equals ulComparand.
*/
static portFORCE_INLINE uint32_t Atomic_CompareAndSwap_u32(
uint32_t volatile * pDestination,
uint32_t ulExchange,
uint32_t ulComparand )
{
uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
if ( __atomic_compare_exchange( pDestination,
&ulComparand,
&ulExchange,
false,
__ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST ) )
{
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
}
#else
ATOMIC_ENTER_CRITICAL();
if ( *pDestination == ulComparand )
{
*pDestination = ulExchange;
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
}
ATOMIC_EXIT_CRITICAL();
#endif
return ulReturnValue;
}
/**
* Atomic swap (pointers)
*
* @brief Atomically sets the address pointed to by *ppDestination to the value
* of *pExchange.
*
* @param[in, out] ppDestination Pointer to memory location from where a pointer
* value is to be loaded and written back to.
* @param[in] pExchange Pointer value to be written to *ppDestination.
*
* @return The initial value of *ppDestination.
*/
static portFORCE_INLINE void * Atomic_SwapPointers_p32(
void * volatile * ppDestination,
void * pExchange )
{
void * pReturnValue;
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
__atomic_exchange( ppDestination, &pExchange, &pReturnValue, __ATOMIC_SEQ_CST );
#else
ATOMIC_ENTER_CRITICAL();
pReturnValue = *ppDestination;
*ppDestination = pExchange;
ATOMIC_EXIT_CRITICAL();
#endif
return pReturnValue;
}
/**
* Atomic compare-and-swap (pointers)
*
* @brief Performs an atomic compare-and-swap operation on the specified pointer
* values.
*
* @param[in, out] ppDestination Pointer to memory location from where a pointer
* value is to be loaded and checked.
* @param[in] pExchange If condition meets, write this value to memory.
* @param[in] pComparand Swap condition.
*
* @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
*
* @note This function only swaps *ppDestination with pExchange, if previous
* *ppDestination value equals pComparand.
*/
static portFORCE_INLINE uint32_t Atomic_CompareAndSwapPointers_p32(
void * volatile * ppDestination,
void * pExchange, void * pComparand )
{
uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
if ( __atomic_compare_exchange( ppDestination,
&pComparand,
&pExchange,
false,
__ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST ) )
{
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
}
#else
ATOMIC_ENTER_CRITICAL();
if ( *ppDestination == pComparand )
{
*ppDestination = pExchange;
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
}
ATOMIC_EXIT_CRITICAL();
#endif
return ulReturnValue;
}
/*----------------------------- Arithmetic ------------------------------*/
/**
* Atomic add
*
* @brief Atomically adds count to the value of the specified pointer points to.
*
* @param[in,out] pAddend Pointer to memory location from where value is to be
* loaded and written back to.
* @param[in] ulCount Value to be added to *pAddend.
*
* @return previous *pAddend value.
*/
static portFORCE_INLINE uint32_t Atomic_Add_u32(
uint32_t volatile * pAddend,
uint32_t ulCount )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_add(pAddend, ulCount, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pAddend;
*pAddend += ulCount;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic subtract
*
* @brief Atomically subtracts count from the value of the specified pointer
* pointers to.
*
* @param[in,out] pAddend Pointer to memory location from where value is to be
* loaded and written back to.
* @param[in] ulCount Value to be subtract from *pAddend.
*
* @return previous *pAddend value.
*/
static portFORCE_INLINE uint32_t Atomic_Subtract_u32(
uint32_t volatile * pAddend,
uint32_t ulCount )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_sub(pAddend, ulCount, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pAddend;
*pAddend -= ulCount;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic increment
*
* @brief Atomically increments the value of the specified pointer points to.
*
* @param[in,out] pAddend Pointer to memory location from where value is to be
* loaded and written back to.
*
* @return *pAddend value before increment.
*/
static portFORCE_INLINE uint32_t Atomic_Increment_u32( uint32_t volatile * pAddend )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_add(pAddend, 1, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pAddend;
*pAddend += 1;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic decrement
*
* @brief Atomically decrements the value of the specified pointer points to
*
* @param[in,out] pAddend Pointer to memory location from where value is to be
* loaded and written back to.
*
* @return *pAddend value before decrement.
*/
static portFORCE_INLINE uint32_t Atomic_Decrement_u32( uint32_t volatile * pAddend )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_sub(pAddend, 1, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pAddend;
*pAddend -= 1;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/*----------------------------- Bitwise Logical ------------------------------*/
/**
* Atomic OR
*
* @brief Performs an atomic OR operation on the specified values.
*
* @param [in, out] pDestination Pointer to memory location from where value is
* to be loaded and written back to.
* @param [in] ulValue Value to be ORed with *pDestination.
*
* @return The original value of *pDestination.
*/
static portFORCE_INLINE uint32_t Atomic_OR_u32(
uint32_t volatile * pDestination,
uint32_t ulValue )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_or(pDestination, ulValue, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pDestination;
*pDestination |= ulValue;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic AND
*
* @brief Performs an atomic AND operation on the specified values.
*
* @param [in, out] pDestination Pointer to memory location from where value is
* to be loaded and written back to.
* @param [in] ulValue Value to be ANDed with *pDestination.
*
* @return The original value of *pDestination.
*/
static portFORCE_INLINE uint32_t Atomic_AND_u32(
uint32_t volatile * pDestination,
uint32_t ulValue )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_and(pDestination, ulValue, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pDestination;
*pDestination &= ulValue;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic NAND
*
* @brief Performs an atomic NAND operation on the specified values.
*
* @param [in, out] pDestination Pointer to memory location from where value is
* to be loaded and written back to.
* @param [in] ulValue Value to be NANDed with *pDestination.
*
* @return The original value of *pDestination.
*/
static portFORCE_INLINE uint32_t Atomic_NAND_u32(
uint32_t volatile * pDestination,
uint32_t ulValue )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_nand(pDestination, ulValue, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pDestination;
*pDestination = ~(ulCurrent & ulValue);
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic XOR
*
* @brief Performs an atomic XOR operation on the specified values.
*
* @param [in, out] pDestination Pointer to memory location from where value is
* to be loaded and written back to.
* @param [in] ulValue Value to be XORed with *pDestination.
*
* @return The original value of *pDestination.
*/
static portFORCE_INLINE uint32_t Atomic_XOR_u32(
uint32_t volatile * pDestination,
uint32_t ulValue )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_xor(pDestination, ulValue, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pDestination;
*pDestination ^= ulValue;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
#ifdef __cplusplus
}
#endif
#endif /* ATOMIC_H */

View file

@ -0,0 +1,39 @@
/*
* Amazon FreeRTOS Common V1.0.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_atomic.h
* @brief Chooses the appropriate atomic operations header.
*
* On FreeRTOS, this file chooses the atomic header provided with the FreeRTOS
* kernel.
*/
#ifndef IOT_ATOMIC_H_
#define IOT_ATOMIC_H_
#include "atomic.h"
#endif /* ifndef IOT_ATOMIC_H_ */

View file

@ -0,0 +1,114 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_error.h
* @brief Provides macros for error checking and function cleanup.
*
* The macros in this file are generic. They may be customized by each library
* by setting the library prefix.
*/
#ifndef IOT_ERROR_H_
#define IOT_ERROR_H_
/* The config header is always included first. */
#include "iot_config.h"
/**
* @brief Declare the status variable and an initial value.
*
* This macro should be at the beginning of any functions that use cleanup sections.
*
* @param[in] statusType The type of the status variable for this function.
* @param[in] initialValue The initial value to assign to the status variable.
*/
#define IOT_FUNCTION_ENTRY( statusType, initialValue ) statusType status = initialValue
/**
* @brief Declares the label that begins a cleanup section.
*
* This macro should be placed at the end of a function and followed by
* #IOT_FUNCTION_CLEANUP_END.
*/
#define IOT_FUNCTION_CLEANUP_BEGIN() iotCleanup:
/**
* @brief Declares the end of a cleanup section.
*
* This macro should be placed at the end of a function and preceded by
* #IOT_FUNCTION_CLEANUP_BEGIN.
*/
#define IOT_FUNCTION_CLEANUP_END() return status
/**
* @brief Declares an empty cleanup section.
*
* This macro should be placed at the end of a function to exit on error if no
* cleanup is required.
*/
#define IOT_FUNCTION_EXIT_NO_CLEANUP() IOT_FUNCTION_CLEANUP_BEGIN(); IOT_FUNCTION_CLEANUP_END()
/**
* @brief Jump to the cleanup section.
*/
#define IOT_GOTO_CLEANUP() goto iotCleanup
/**
* @brief Assign a value to the status variable and jump to the cleanup section.
*
* @param[in] statusValue The value to assign to the status variable.
*/
#define IOT_SET_AND_GOTO_CLEANUP( statusValue ) { status = ( statusValue ); IOT_GOTO_CLEANUP(); }
/**
* @brief Jump to the cleanup section if a condition is `false`.
*
* This macro may be used in place of `assert` to exit a function is a condition
* is `false`.
*
* @param[in] condition The condition to check.
*/
#define IOT_GOTO_CLEANUP_IF_FALSE( condition ) { if( ( condition ) == false ) { IOT_GOTO_CLEANUP(); } }
/**
* @brief Assign a value to the status variable and jump to the cleanup section
* if a condition is `false`.
*
* @param[in] statusValue The value to assign to the status variable.
* @param[in] condition The condition to check.
*/
#define IOT_SET_AND_GOTO_CLEANUP_IF_FALSE( statusValue, condition ) \
if( ( condition ) == false ) \
IOT_SET_AND_GOTO_CLEANUP( statusValue )
/**
* @brief Check a condition; if `false`, assign the "Bad parameter" status value
* and jump to the cleanup section.
*
* @param[in] libraryPrefix The library prefix of the status variable.
* @param[in] condition The condition to check.
*/
#define IOT_VALIDATE_PARAMETER( libraryPrefix, condition ) \
IOT_SET_AND_GOTO_CLEANUP_IF_FALSE( libraryPrefix ## _BAD_PARAMETER, condition )
#endif /* ifndef IOT_ERROR_H_ */

View file

@ -0,0 +1,64 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_init.h
* @brief Provides function signatures for common initialization and cleanup of
* this SDK.
*/
#ifndef IOT_INIT_H_
#define IOT_INIT_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
/**
* @brief One-time initialization function for this SDK.
*
* This function initializes common libraries, such as static memory and task
* pool. <b>It must be called once (and only once) before calling any other
* function in this SDK.</b> Calling this function more than once without first
* calling `IotSdk_Cleanup` may result in a crash.
*
* @return `true` if initialization succeeded; `false` otherwise. Logs may be
* printed in case of failure.
*
* @warning No thread-safety guarantees are provided for this function.
*/
bool IotSdk_Init( void );
/**
* @brief One-time deinitialization function for all common libraries.
*
* This function frees resources taken in `IotSdk_Init`. No other function
* in this SDK may be called after calling this function unless `IotSdk_Init`
* is called again.
*
* @warning No thread-safety guarantees are provided for this function.
*/
void IotSdk_Cleanup( void );
#endif /* IOT_INIT_H_ */

View file

@ -0,0 +1,956 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_linear_containers.h
* @brief Declares and implements doubly-linked lists and queues.
*/
#ifndef IOT_LINEAR_CONTAINERS_H_
#define IOT_LINEAR_CONTAINERS_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* @defgroup linear_containers_datatypes_listqueue List and queue
* @brief Structures that represent a list or queue.
*/
/**
* @ingroup linear_containers_datatypes_listqueue
* @brief Link member placed in structs of a list or queue.
*
* All elements in a list or queue must contain one of these members. The macro
* #IotLink_Container can be used to calculate the starting address of the
* link's container.
*/
typedef struct IotLink
{
struct IotLink * pPrevious; /**< @brief Pointer to the previous element. */
struct IotLink * pNext; /**< @brief Pointer to the next element. */
} IotLink_t;
/**
* @ingroup linear_containers_datatypes_listqueue
* @brief Represents a doubly-linked list.
*/
typedef IotLink_t IotListDouble_t;
/**
* @ingroup linear_containers_datatypes_listqueue
* @brief Represents a queue.
*/
typedef IotLink_t IotDeQueue_t;
/**
* @constantspage{linear_containers,linear containers library}
*
* @section linear_containers_constants_initializers Linear Containers Initializers
* @brief Provides default values for initializing the linear containers data types.
*
* @snippet this define_linear_containers_initializers
*
* All user-facing data types of the linear containers library should be initialized
* using one of the following.
*
* @warning Failure to initialize a linear containers data type with the appropriate
* initializer may result in a runtime error!
* @note The initializers may change at any time in future versions, but their
* names will remain the same.
*/
/* @[define_linear_containers_initializers] */
#define IOT_LINK_INITIALIZER { 0 } /**< @brief Initializer for an #IotLink_t. */
#define IOT_LIST_DOUBLE_INITIALIZER IOT_LINK_INITIALIZER /**< @brief Initializer for an #IotListDouble_t. */
#define IOT_DEQUEUE_INITIALIZER IOT_LINK_INITIALIZER /**< @brief Initializer for an #IotDeQueue_t. */
/* @[define_linear_containers_initializers] */
/**
* @def IotContainers_Assert( expression )
* @brief Assertion macro for the linear containers library.
*
* Set @ref IOT_CONTAINERS_ENABLE_ASSERTS to `1` to enable assertions in the linear
* containers library.
*
* @param[in] expression Expression to be evaluated.
*/
#if IOT_CONTAINERS_ENABLE_ASSERTS == 1
#ifndef IotContainers_Assert
#ifdef Iot_DefaultAssert
#define IotContainers_Assert( expression ) Iot_DefaultAssert( expression )
#else
#error "Asserts are enabled for containers, but IotContainers_Assert is not defined"
#endif
#endif
#else /* if IOT_CONTAINERS_ENABLE_ASSERTS == 1 */
#define IotContainers_Assert( expression )
#endif /* if IOT_CONTAINERS_ENABLE_ASSERTS == 1 */
/**
* @brief Calculates the starting address of a containing struct.
*
* @param[in] type Type of the containing struct.
* @param[in] pLink Pointer to a link member.
* @param[in] linkName Name of the #IotLink_t in the containing struct.
*/
#define IotLink_Container( type, pLink, linkName ) \
( ( type * ) ( void * ) ( ( ( uint8_t * ) ( pLink ) ) - offsetof( type, linkName ) ) )
/**
* @brief Iterates through all elements of a linear container.
*
* Container elements must not be freed or removed while iterating.
*
* @param[in] pStart The first element to iterate from.
* @param[out] pLink Pointer to a container element.
*/
#define IotContainers_ForEach( pStart, pLink ) \
for( ( pLink ) = ( pStart )->pNext; \
( pLink ) != ( pStart ); \
( pLink ) = ( pLink )->pNext )
/**
* @functionspage{linear_containers,linear containers library}
* - @functionname{linear_containers_function_link_islinked}
* - @functionname{linear_containers_function_list_double_create}
* - @functionname{linear_containers_function_list_double_count}
* - @functionname{linear_containers_function_list_double_isempty}
* - @functionname{linear_containers_function_list_double_peekhead}
* - @functionname{linear_containers_function_list_double_peektail}
* - @functionname{linear_containers_function_list_double_inserthead}
* - @functionname{linear_containers_function_list_double_inserttail}
* - @functionname{linear_containers_function_list_double_insertbefore}
* - @functionname{linear_containers_function_list_double_insertafter}
* - @functionname{linear_containers_function_list_double_insertsorted}
* - @functionname{linear_containers_function_list_double_remove}
* - @functionname{linear_containers_function_list_double_removehead}
* - @functionname{linear_containers_function_list_double_removetail}
* - @functionname{linear_containers_function_list_double_removeall}
* - @functionname{linear_containers_function_list_double_findfirstmatch}
* - @functionname{linear_containers_function_list_double_removefirstmatch}
* - @functionname{linear_containers_function_list_double_removeallmatches}
* - @functionname{linear_containers_function_queue_create}
* - @functionname{linear_containers_function_queue_count}
* - @functionname{linear_containers_function_queue_isempty}
* - @functionname{linear_containers_function_queue_peekhead}
* - @functionname{linear_containers_function_queue_peektail}
* - @functionname{linear_containers_function_queue_enqueuehead}
* - @functionname{linear_containers_function_queue_dequeuehead}
* - @functionname{linear_containers_function_queue_enqueuetail}
* - @functionname{linear_containers_function_queue_dequeuetail}
* - @functionname{linear_containers_function_queue_remove}
* - @functionname{linear_containers_function_queue_removeall}
* - @functionname{linear_containers_function_queue_removeallmatches}
*/
/**
* @functionpage{IotLink_IsLinked,linear_containers,link_islinked}
* @functionpage{IotListDouble_Create,linear_containers,list_double_create}
* @functionpage{IotListDouble_Count,linear_containers,list_double_count}
* @functionpage{IotListDouble_IsEmpty,linear_containers,list_double_isempty}
* @functionpage{IotListDouble_PeekHead,linear_containers,list_double_peekhead}
* @functionpage{IotListDouble_PeekTail,linear_containers,list_double_peektail}
* @functionpage{IotListDouble_InsertHead,linear_containers,list_double_inserthead}
* @functionpage{IotListDouble_InsertTail,linear_containers,list_double_inserttail}
* @functionpage{IotListDouble_InsertBefore,linear_containers,list_double_insertbefore}
* @functionpage{IotListDouble_InsertAfter,linear_containers,list_double_insertafter}
* @functionpage{IotListDouble_InsertSorted,linear_containers,list_double_insertsorted}
* @functionpage{IotListDouble_Remove,linear_containers,list_double_remove}
* @functionpage{IotListDouble_RemoveHead,linear_containers,list_double_removehead}
* @functionpage{IotListDouble_RemoveTail,linear_containers,list_double_removetail}
* @functionpage{IotListDouble_RemoveAll,linear_containers,list_double_removeall}
* @functionpage{IotListDouble_FindFirstMatch,linear_containers,list_double_findfirstmatch}
* @functionpage{IotListDouble_RemoveFirstMatch,linear_containers,list_double_removefirstmatch}
* @functionpage{IotListDouble_RemoveAllMatches,linear_containers,list_double_removeallmatches}
* @functionpage{IotDeQueue_Create,linear_containers,queue_create}
* @functionpage{IotDeQueue_Count,linear_containers,queue_count}
* @functionpage{IotDeQueue_IsEmpty,linear_containers,queue_isempty}
* @functionpage{IotDeQueue_PeekHead,linear_containers,queue_peekhead}
* @functionpage{IotDeQueue_PeekTail,linear_containers,queue_peektail}
* @functionpage{IotDeQueue_EnqueueHead,linear_containers,queue_enqueuehead}
* @functionpage{IotDeQueue_DequeueHead,linear_containers,queue_dequeuehead}
* @functionpage{IotDeQueue_EnqueueTail,linear_containers,queue_enqueuetail}
* @functionpage{IotDeQueue_DequeueTail,linear_containers,queue_dequeuetail}
* @functionpage{IotDeQueue_Remove,linear_containers,queue_remove}
* @functionpage{IotDeQueue_RemoveAll,linear_containers,queue_removeall}
* @functionpage{IotDeQueue_RemoveAllMatches,linear_containers,queue_removeallmatches}
*/
/**
* @brief Check if an #IotLink_t is linked in a list or queue.
*
* @param[in] pLink The link to check.
*
* @return `true` if `pCurrent` is linked in a list or queue; `false` otherwise.
*/
/* @[declare_linear_containers_link_islinked] */
static inline bool IotLink_IsLinked( const IotLink_t * const pLink )
/* @[declare_linear_containers_link_islinked] */
{
bool isLinked = false;
if( pLink != NULL )
{
isLinked = ( pLink->pNext != NULL ) && ( pLink->pPrevious != NULL );
}
return isLinked;
}
/**
* @brief Create a new doubly-linked list.
*
* This function initializes a new doubly-linked list. It must be called on an
* uninitialized #IotListDouble_t before calling any other doubly-linked list
* function. This function must not be called on an already-initialized
* #IotListDouble_t.
*
* This function will not fail. The function @ref linear_containers_function_list_double_removeall
* may be called to destroy a list.
*
* @param[in] pList Pointer to the memory that will hold the new doubly-linked list.
*/
/* @[declare_linear_containers_list_double_create] */
static inline void IotListDouble_Create( IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_create] */
{
/* This function must not be called with a NULL parameter. */
IotContainers_Assert( pList != NULL );
/* An empty list is a link pointing to itself. */
pList->pPrevious = pList;
pList->pNext = pList;
}
/**
* @brief Return the number of elements contained in an #IotListDouble_t.
*
* @param[in] pList The doubly-linked list with the elements to count.
*
* @return The number of elements in the doubly-linked list.
*/
/* @[declare_linear_containers_list_double_count] */
static inline size_t IotListDouble_Count( const IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_count] */
{
size_t count = 0;
if( pList != NULL )
{
/* Get the list head. */
const IotLink_t * pCurrent = pList->pNext;
/* Iterate through the list to count the elements. */
while( pCurrent != pList )
{
count++;
pCurrent = pCurrent->pNext;
}
}
return count;
}
/**
* @brief Check if a doubly-linked list is empty.
*
* @param[in] pList The doubly-linked list to check.
*
* @return `true` if the list is empty; `false` otherwise.
*/
/* @[declare_linear_containers_list_double_isempty] */
static inline bool IotListDouble_IsEmpty( const IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_isempty] */
{
/* An empty list is NULL link, or a link pointing to itself. */
return( ( pList == NULL ) || ( pList->pNext == pList ) );
}
/**
* @brief Return an #IotLink_t representing the first element in a doubly-linked list
* without removing it.
*
* @param[in] pList The list to peek.
*
* @return Pointer to an #IotLink_t representing the element at the head of the
* list; `NULL` if the list is empty. The macro #IotLink_Container may be used to
* determine the address of the link's container.
*/
/* @[declare_linear_containers_list_double_peekhead] */
static inline IotLink_t * IotListDouble_PeekHead( const IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_peekhead] */
{
IotLink_t * pHead = NULL;
if( pList != NULL )
{
if( IotListDouble_IsEmpty( pList ) == false )
{
pHead = pList->pNext;
}
}
return pHead;
}
/**
* @brief Return an #IotLink_t representing the last element in a doubly-linked
* list without removing it.
*
* @param[in] pList The list to peek.
*
* @return Pointer to an #IotLink_t representing the element at the tail of the
* list; `NULL` if the list is empty. The macro #IotLink_Container may be used to
* determine the address of the link's container.
*/
/* @[declare_linear_containers_list_double_peektail] */
static inline IotLink_t * IotListDouble_PeekTail( const IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_peektail] */
{
IotLink_t * pTail = NULL;
if( pList != NULL )
{
if( IotListDouble_IsEmpty( pList ) == false )
{
pTail = pList->pPrevious;
}
}
return pTail;
}
/**
* @brief Insert an element at the head of a doubly-linked list.
*
* @param[in] pList The doubly-linked list that will hold the new element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_list_double_inserthead] */
static inline void IotListDouble_InsertHead( IotListDouble_t * const pList,
IotLink_t * const pLink )
/* @[declare_linear_containers_list_double_inserthead] */
{
/* This function must not be called with NULL parameters. */
IotContainers_Assert( pList != NULL );
IotContainers_Assert( pLink != NULL );
/* Save current list head. */
IotLink_t * pHead = pList->pNext;
/* Place new element before list head. */
pLink->pNext = pHead;
pLink->pPrevious = pList;
/* Assign new list head. */
pHead->pPrevious = pLink;
pList->pNext = pLink;
}
/**
* @brief Insert an element at the tail of a doubly-linked list.
*
* @param[in] pList The double-linked list that will hold the new element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_list_double_inserttail] */
static inline void IotListDouble_InsertTail( IotListDouble_t * const pList,
IotLink_t * const pLink )
/* @[declare_linear_containers_list_double_inserttail] */
{
/* This function must not be called with NULL parameters. */
IotContainers_Assert( pList != NULL );
IotContainers_Assert( pLink != NULL );
/* Save current list tail. */
IotLink_t * pTail = pList->pPrevious;
pLink->pNext = pList;
pLink->pPrevious = pTail;
pList->pPrevious = pLink;
pTail->pNext = pLink;
}
/**
* @brief Insert an element before another element in a doubly-linked list.
*
* @param[in] pElement The new element will be placed before this element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_list_double_insertbefore] */
static inline void IotListDouble_InsertBefore( IotLink_t * const pElement,
IotLink_t * const pLink )
/* @[declare_linear_containers_list_double_insertbefore] */
{
IotListDouble_InsertTail( pElement, pLink );
}
/**
* @brief Insert an element after another element in a doubly-linked list.
*
* @param[in] pElement The new element will be placed after this element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_list_double_insertafter] */
static inline void IotListDouble_InsertAfter( IotLink_t * const pElement,
IotLink_t * const pLink )
/* @[declare_linear_containers_list_double_insertafter] */
{
IotListDouble_InsertHead( pElement, pLink );
}
/**
* @brief Insert an element in a sorted doubly-linked list.
*
* Places an element into a list by sorting it into order. The function
* `compare` is used to determine where to place the new element.
*
* @param[in] pList The list that will hold the new element.
* @param[in] pLink Pointer to the new element's link member.
* @param[in] compare Determines the order of the list. Returns a negative
* value if its first argument is less than its second argument; returns
* zero if its first argument is equal to its second argument; returns a
* positive value if its first argument is greater than its second argument.
* The parameters to this function are #IotLink_t, so the macro #IotLink_Container
* may be used to determine the address of the link's container.
*/
/* @[declare_linear_containers_list_double_insertsorted] */
static inline void IotListDouble_InsertSorted( IotListDouble_t * const pList,
IotLink_t * const pLink,
int32_t ( *compare )( const IotLink_t * const, const IotLink_t * const ) )
/* @[declare_linear_containers_list_double_insertsorted] */
{
/* This function must not be called with NULL parameters. */
IotContainers_Assert( pList != NULL );
IotContainers_Assert( pLink != NULL );
IotContainers_Assert( compare != NULL );
/* Insert at head for empty list. */
if( IotListDouble_IsEmpty( pList ) == true )
{
IotListDouble_InsertHead( pList, pLink );
}
else
{
bool inserted = false;
IotLink_t * pCurrent = pList->pNext;
/* Iterate through the list to find the correct position. */
while( pCurrent != pList )
{
/* Comparing for '<' preserves the order of insertion. */
if( compare( pLink, pCurrent ) < 0 )
{
IotListDouble_InsertBefore( pCurrent, pLink );
inserted = true;
break;
}
pCurrent = pCurrent->pNext;
}
/* New element is greater than all elements in list. Insert at tail. */
if( inserted == false )
{
IotListDouble_InsertTail( pList, pLink );
}
}
}
/**
* @brief Remove a single element from a doubly-linked list.
*
* @param[in] pLink The element to remove.
*/
/* @[declare_linear_containers_list_double_remove] */
static inline void IotListDouble_Remove( IotLink_t * const pLink )
/* @[declare_linear_containers_list_double_remove] */
{
/* This function must not be called with a NULL parameter. */
IotContainers_Assert( pLink != NULL );
/* This function must be called on a linked element. */
IotContainers_Assert( IotLink_IsLinked( pLink ) == true );
pLink->pPrevious->pNext = pLink->pNext;
pLink->pNext->pPrevious = pLink->pPrevious;
pLink->pPrevious = NULL;
pLink->pNext = NULL;
}
/**
* @brief Remove the element at the head of a doubly-linked list.
*
* @param[in] pList The doubly-linked list that holds the element to remove.
*
* @return Pointer to an #IotLink_t representing the removed list head; `NULL`
* if the list is empty. The macro #IotLink_Container may be used to determine
* the address of the link's container.
*/
/* @[declare_linear_containers_list_double_removehead] */
static inline IotLink_t * IotListDouble_RemoveHead( IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_removehead] */
{
IotLink_t * pHead = NULL;
if( IotListDouble_IsEmpty( pList ) == false )
{
pHead = pList->pNext;
IotListDouble_Remove( pHead );
}
return pHead;
}
/**
* @brief Remove the element at the tail of a doubly-linked list.
*
* @param[in] pList The doubly-linked list that holds the element to remove.
*
* @return Pointer to an #IotLink_t representing the removed list tail; `NULL`
* if the list is empty. The macro #IotLink_Container may be used to determine
* the address of the link's container.
*/
/* @[declare_linear_containers_list_double_removetail] */
static inline IotLink_t * IotListDouble_RemoveTail( IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_removetail] */
{
IotLink_t * pTail = NULL;
if( IotListDouble_IsEmpty( pList ) == false )
{
pTail = pList->pPrevious;
IotListDouble_Remove( pTail );
}
return pTail;
}
/**
* @brief Remove all elements in a doubly-linked list.
*
* @param[in] pList The list to empty.
* @param[in] freeElement A function to free memory used by each removed list
* element. Optional; pass `NULL` to ignore.
* @param[in] linkOffset Offset in bytes of a link member in its container, used
* to calculate the pointer to pass to `freeElement`. This value should be calculated
* with the C `offsetof` macro. This parameter is ignored if `freeElement` is `NULL`
* or its value is `0`.
*/
/* @[declare_linear_containers_list_double_removeall] */
static inline void IotListDouble_RemoveAll( IotListDouble_t * const pList,
void ( *freeElement )( void * ),
size_t linkOffset )
/* @[declare_linear_containers_list_double_removeall] */
{
/* This function must not be called with a NULL pList parameter. */
IotContainers_Assert( pList != NULL );
/* Get the list head. */
IotLink_t * pCurrent = pList->pNext;
/* Iterate through the list and remove all elements. */
while( pCurrent != pList )
{
/* Save a pointer to the next list element. */
IotLink_t * pNext = pCurrent->pNext;
/* Remove and free the current list element. */
IotListDouble_Remove( pCurrent );
if( freeElement != NULL )
{
freeElement( ( ( uint8_t * ) pCurrent ) - linkOffset );
}
/* Move the iterating pointer to the next list element. */
pCurrent = pNext;
}
}
/**
* @brief Search a doubly-linked list for the first matching element.
*
* If a match is found, the matching element is <b>not</b> removed from the list.
* See @ref linear_containers_function_list_double_removefirstmatch for the function
* that searches and removes.
*
* @param[in] pList The doubly-linked list to search.
* @param[in] pStartPoint An element in `pList`. Only elements between this one and
* the list tail are checked. Pass `NULL` to search from the beginning of the list.
* @param[in] isMatch Function to determine if an element matches. Pass `NULL` to
* search using the address `pMatch`, i.e. `element == pMatch`.
* @param[in] pMatch If `isMatch` is `NULL`, each element in the list is compared
* to this address to find a match. Otherwise, it is passed as the second argument
* to `isMatch`.
*
* @return Pointer to an #IotLink_t representing the first matched element; `NULL`
* if no match is found. The macro #IotLink_Container may be used to determine the
* address of the link's container.
*/
/* @[declare_linear_containers_list_double_findfirstmatch] */
static inline IotLink_t * IotListDouble_FindFirstMatch( const IotListDouble_t * const pList,
const IotLink_t * const pStartPoint,
bool ( *isMatch )( const IotLink_t * const, void * ),
void * pMatch )
/* @[declare_linear_containers_list_double_findfirstmatch] */
{
/* The const must be cast away to match this function's return value. Nevertheless,
* this function will respect the const-ness of pStartPoint. */
IotLink_t * pCurrent = ( IotLink_t * ) pStartPoint;
/* This function must not be called with a NULL pList parameter. */
IotContainers_Assert( pList != NULL );
/* Search starting from list head if no start point is given. */
if( pStartPoint == NULL )
{
pCurrent = pList->pNext;
}
/* Iterate through the list to search for matches. */
while( pCurrent != pList )
{
/* Call isMatch if provided. Otherwise, compare pointers. */
if( isMatch != NULL )
{
if( isMatch( pCurrent, pMatch ) == true )
{
return pCurrent;
}
}
else
{
if( pCurrent == pMatch )
{
return pCurrent;
}
}
pCurrent = pCurrent->pNext;
}
/* No match found, return NULL. */
return NULL;
}
/**
* @brief Search a doubly-linked list for the first matching element and remove
* it.
*
* An #IotLink_t may be passed as `pList` to start searching after the head of a
* doubly-linked list.
*
* @param[in] pList The doubly-linked list to search.
* @param[in] pStartPoint An element in `pList`. Only elements between this one and
* the list tail are checked. Pass `NULL` to search from the beginning of the list.
* @param[in] isMatch Function to determine if an element matches. Pass `NULL` to
* search using the address `pMatch`, i.e. `element == pMatch`.
* @param[in] pMatch If `isMatch` is `NULL`, each element in the list is compared
* to this address to find a match. Otherwise, it is passed as the second argument
* to `isMatch`.
*
* @return Pointer to an #IotLink_t representing the matched and removed element;
* `NULL` if no match is found. The macro #IotLink_Container may be used to determine
* the address of the link's container.
*/
/* @[declare_linear_containers_list_double_removefirstmatch] */
static inline IotLink_t * IotListDouble_RemoveFirstMatch( IotListDouble_t * const pList,
const IotLink_t * const pStartPoint,
bool ( *isMatch )( const IotLink_t *, void * ),
void * pMatch )
/* @[declare_linear_containers_list_double_removefirstmatch] */
{
IotLink_t * pMatchedElement = IotListDouble_FindFirstMatch( pList,
pStartPoint,
isMatch,
pMatch );
if( pMatchedElement != NULL )
{
IotListDouble_Remove( pMatchedElement );
}
return pMatchedElement;
}
/**
* @brief Remove all matching elements from a doubly-linked list.
*
* @param[in] pList The doubly-linked list to search.
* @param[in] isMatch Function to determine if an element matches. Pass `NULL` to
* search using the address `pMatch`, i.e. `element == pMatch`.
* @param[in] pMatch If `isMatch` is `NULL`, each element in the list is compared
* to this address to find a match. Otherwise, it is passed as the second argument
* to `isMatch`.
* @param[in] freeElement A function to free memory used by each removed list
* element. Optional; pass `NULL` to ignore.
* @param[in] linkOffset Offset in bytes of a link member in its container, used
* to calculate the pointer to pass to `freeElement`. This value should be calculated
* with the C `offsetof` macro. This parameter is ignored if `freeElement` is `NULL`
* or its value is `0`.
*/
/* @[declare_linear_containers_list_double_removeallmatches] */
static inline void IotListDouble_RemoveAllMatches( IotListDouble_t * const pList,
bool ( *isMatch )( const IotLink_t *, void * ),
void * pMatch,
void ( *freeElement )( void * ),
size_t linkOffset )
/* @[declare_linear_containers_list_double_removeallmatches] */
{
IotLink_t * pMatchedElement = NULL, * pNextElement = NULL;
/* Search the list for all matching elements. */
do
{
pMatchedElement = IotListDouble_FindFirstMatch( pList,
pMatchedElement,
isMatch,
pMatch );
if( pMatchedElement != NULL )
{
/* Save pointer to next element. */
pNextElement = pMatchedElement->pNext;
/* Match found; remove and free. */
IotListDouble_Remove( pMatchedElement );
if( freeElement != NULL )
{
freeElement( ( ( uint8_t * ) pMatchedElement ) - linkOffset );
}
/* Continue search from next element. */
pMatchedElement = pNextElement;
}
} while( pMatchedElement != NULL );
}
/**
* @brief Create a new queue.
*
* This function initializes a new double-ended queue. It must be called on an uninitialized
* #IotDeQueue_t before calling any other queue function. This function must not be
* called on an already-initialized #IotDeQueue_t.
*
* This function will not fail.
*
* @param[in] pQueue Pointer to the memory that will hold the new queue.
*/
/* @[declare_linear_containers_queue_create] */
static inline void IotDeQueue_Create( IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_create] */
{
IotListDouble_Create( pQueue );
}
/**
* @brief Return the number of elements contained in an #IotDeQueue_t.
*
* @param[in] pQueue The queue with the elements to count.
*
* @return The number of items elements in the queue.
*/
/* @[declare_linear_containers_queue_count] */
static inline size_t IotDeQueue_Count( const IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_count] */
{
return IotListDouble_Count( pQueue );
}
/**
* @brief Check if a queue is empty.
*
* @param[in] pQueue The queue to check.
*
* @return `true` if the queue is empty; `false` otherwise.
*
*/
/* @[declare_linear_containers_queue_isempty] */
static inline bool IotDeQueue_IsEmpty( const IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_isempty] */
{
return IotListDouble_IsEmpty( pQueue );
}
/**
* @brief Return an #IotLink_t representing the element at the front of the queue
* without removing it.
*
* @param[in] pQueue The queue to peek.
*
* @return Pointer to an #IotLink_t representing the element at the head of the
* queue; `NULL` if the queue is empty. The macro #IotLink_Container may be used
* to determine the address of the link's container.
*/
/* @[declare_linear_containers_queue_peekhead] */
static inline IotLink_t * IotDeQueue_PeekHead( const IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_peekhead] */
{
return IotListDouble_PeekHead( pQueue );
}
/**
* @brief Return an #IotLink_t representing the element at the back of the queue
* without removing it.
*
* @param[in] pQueue The queue to peek.
*
* @return Pointer to an #IotLink_t representing the element at the head of the
* queue; `NULL` if the queue is empty. The macro #IotLink_Container may be used
* to determine the address of the link's container.
*/
/* @[declare_linear_containers_queue_peektail] */
static inline IotLink_t * IotDeQueue_PeekTail( const IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_peektail] */
{
return IotListDouble_PeekTail( pQueue );
}
/**
* @brief Add an element at the head of the queue.
*
* @param[in] pQueue The queue that will hold the new element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_queue_enqueuehead] */
static inline void IotDeQueue_EnqueueHead( IotDeQueue_t * const pQueue,
IotLink_t * const pLink )
/* @[declare_linear_containers_queue_enqueuehead] */
{
IotListDouble_InsertHead( pQueue, pLink );
}
/**
* @brief Remove an element at the head of the queue.
*
* @param[in] pQueue The queue that holds the element to remove.
*
* @return Pointer to an #IotLink_t representing the removed queue element; `NULL`
* if the queue is empty. The macro #IotLink_Container may be used to determine
* the address of the link's container.
*/
/* @[declare_linear_containers_queue_dequeuehead] */
static inline IotLink_t * IotDeQueue_DequeueHead( IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_dequeuehead] */
{
return IotListDouble_RemoveHead( pQueue );
}
/**
* @brief Add an element at the tail of the queue.
*
* @param[in] pQueue The queue that will hold the new element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_queue_enqueuetail] */
static inline void IotDeQueue_EnqueueTail( IotDeQueue_t * const pQueue,
IotLink_t * const pLink )
/* @[declare_linear_containers_queue_enqueuetail] */
{
IotListDouble_InsertTail( pQueue, pLink );
}
/**
* @brief Remove an element at the tail of the queue.
*
* @param[in] pQueue The queue that holds the element to remove.
*
* @return Pointer to an #IotLink_t representing the removed queue element; `NULL`
* if the queue is empty. The macro #IotLink_Container may be used to determine
* the address of the link's container.
*/
/* @[declare_linear_containers_queue_dequeuetail] */
static inline IotLink_t * IotDeQueue_DequeueTail( IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_dequeuetail] */
{
return IotListDouble_RemoveTail( pQueue );
}
/**
* @brief Remove a single element from a queue.
*
* @param[in] pLink The element to remove.
*/
/* @[declare_linear_containers_queue_remove] */
static inline void IotDeQueue_Remove( IotLink_t * const pLink )
/* @[declare_linear_containers_queue_remove] */
{
IotListDouble_Remove( pLink );
}
/**
* @brief Remove all elements in a queue.
*
* @param[in] pQueue The queue to empty.
* @param[in] freeElement A function to free memory used by each removed queue
* element. Optional; pass `NULL` to ignore.
* @param[in] linkOffset Offset in bytes of a link member in its container, used
* to calculate the pointer to pass to `freeElement`. This value should be calculated
* with the C `offsetof` macro. This parameter is ignored if `freeElement` is `NULL`
* or its value is `0`.
*/
/* @[declare_linear_containers_queue_removeall] */
static inline void IotDeQueue_RemoveAll( IotDeQueue_t * const pQueue,
void ( * freeElement )( void * ),
size_t linkOffset )
/* @[declare_linear_containers_queue_removeall] */
{
IotListDouble_RemoveAll( pQueue, freeElement, linkOffset );
}
/**
* @brief Remove all matching elements from a queue.
*
* @param[in] pQueue The queue to search.
* @param[in] isMatch Function to determine if an element matches. Pass `NULL` to
* search using the address `pMatch`, i.e. `element == pMatch`.
* @param[in] pMatch If `isMatch` is `NULL`, each element in the queue is compared
* to this address to find a match. Otherwise, it is passed as the second argument
* to `isMatch`.
* @param[in] freeElement A function to free memory used by each removed queue
* element. Optional; pass `NULL` to ignore.
* @param[in] linkOffset Offset in bytes of a link member in its container, used
* to calculate the pointer to pass to `freeElement`. This value should be calculated
* with the C `offsetof` macro. This parameter is ignored if `freeElement` is `NULL`
* or its value is `0`.
*/
/* @[declare_linear_containers_queue_removeallmatches] */
static inline void IotDeQueue_RemoveAllMatches( IotDeQueue_t * const pQueue,
bool ( * isMatch )( const IotLink_t *, void * ),
void * pMatch,
void ( * freeElement )( void * ),
size_t linkOffset )
/* @[declare_linear_containers_queue_removeallmatches] */
{
IotListDouble_RemoveAllMatches( pQueue, isMatch, pMatch, freeElement, linkOffset );
}
#endif /* IOT_LINEAR_CONTAINERS_H_ */

View file

@ -0,0 +1,226 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_logging.h
* @brief Generic logging function header file.
*
* Declares the generic logging function and the log levels. This file never
* needs to be included in source code. The header iot_logging_setup.h should
* be included instead.
*
* @see iot_logging_setup.h
*/
#ifndef IOT_LOGGING_H_
#define IOT_LOGGING_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* @constantspage{logging,logging library}
*
* @section logging_constants_levels Log levels
* @brief Log levels for the libraries in this SDK.
*
* Each library should specify a log level by setting @ref LIBRARY_LOG_LEVEL.
* All log messages with a level at or below the specified level will be printed
* for that library.
*
* Currently, there are 4 log levels. In the order of lowest to highest, they are:
* - #IOT_LOG_NONE <br>
* @copybrief IOT_LOG_NONE
* - #IOT_LOG_ERROR <br>
* @copybrief IOT_LOG_ERROR
* - #IOT_LOG_WARN <br>
* @copybrief IOT_LOG_WARN
* - #IOT_LOG_INFO <br>
* @copybrief IOT_LOG_INFO
* - #IOT_LOG_DEBUG <br>
* @copybrief IOT_LOG_DEBUG
*/
/**
* @brief No log messages.
*
* Log messages with this level will be silently discarded. When @ref
* LIBRARY_LOG_LEVEL is #IOT_LOG_NONE, logging is disabled and no [logging functions]
* (@ref logging_functions) can be called.
*/
#define IOT_LOG_NONE 0
/**
* @brief Only critical, unrecoverable errors.
*
* Log messages with this level will be printed when a library encounters an
* error from which it cannot easily recover.
*/
#define IOT_LOG_ERROR 1
/**
* @brief Message about an abnormal but recoverable event.
*
* Log messages with this level will be printed when a library encounters an
* abnormal event that may be indicative of an error. Libraries should continue
* execution after logging a warning.
*/
#define IOT_LOG_WARN 2
/**
* @brief A helpful, informational message.
*
* Log messages with this level may indicate the normal status of a library
* function. They should be used to track how far a program has executed.
*/
#define IOT_LOG_INFO 3
/**
* @brief Detailed and excessive debug information.
*
* Log messages with this level are intended for developers. They may contain
* excessive information such as internal variables, buffers, or other specific
* information.
*/
#define IOT_LOG_DEBUG 4
/**
* @paramstructs{logging,logging}
*/
/**
* @ingroup logging_datatypes_paramstructs
* @brief Log message configuration struct.
*
* @paramfor @ref logging_function_log, @ref logging_function_generic
*
* By default, log messages print the library name, log level, and a timestring.
* This struct can be passed to @ref logging_function_generic to disable one of
* the above components in the log message.
*
* <b>Example:</b>
*
* @code{c}
* IotLog_Generic( IOT_LOG_DEBUG, "SAMPLE", IOT_LOG_DEBUG, NULL, "Hello world!" );
* @endcode
* The code above prints the following message:
* @code
* [DEBUG][SAMPLE][2018-01-01 12:00:00] Hello world!
* @endcode
*
* The timestring can be disabled as follows:
* @code
* IotLogConfig_t logConfig = { .hideLogLevel = false, .hideLibraryName = false, .hideTimestring = true};
* IotLog_Generic( IOT_LOG_DEBUG, "SAMPLE", IOT_LOG_DEBUG, &logConfig, "Hello world!" );
* @endcode
* The resulting log message will be:
* @code
* [DEBUG][SAMPLE] Hello world!
* @endcode
*/
typedef struct IotLogConfig
{
bool hideLogLevel; /**< @brief Don't print the log level string for this message. */
bool hideLibraryName; /**< @brief Don't print the library name for this message. */
bool hideTimestring; /**< @brief Don't print the timestring for this message. */
} IotLogConfig_t;
/**
* @functionspage{logging,logging library}
*
* - @functionname{logging_function_log}
* - @functionname{logging_function_printbuffer}
* - @functionname{logging_function_generic}
* - @functionname{logging_function_genericprintbuffer}
*/
/**
* @functionpage{IotLog_Generic,logging,generic}
* @functionpage{IotLog_PrintBuffer,logging,genericprintbuffer}
*/
/**
* @brief Generic logging function that prints a single message.
*
* This function is the generic logging function shared across all libraries.
* The library-specific logging function @ref logging_function_log is implemented
* using this function. Like @ref logging_function_log, this function is only
* available when @ref LIBRARY_LOG_LEVEL is #IOT_LOG_NONE.
*
* In most cases, the library-specific logging function @ref logging_function_log
* should be called instead of this function.
*
* @param[in] libraryLogSetting The log level setting of the library, used to
* determine if the log message should be printed. Must be one of the @ref
* logging_constants_levels.
* @param[in] pLibraryName The library name to print. See @ref LIBRARY_LOG_NAME.
* @param[in] messageLevel The log level of the this message. See @ref LIBRARY_LOG_LEVEL.
* @param[in] pLogConfig Pointer to a #IotLogConfig_t. Optional; pass `NULL` to ignore.
* @param[in] pFormat Format string for the log message.
* @param[in] ... Arguments for format specification.
*
* @return No return value. On errors, it prints nothing.
*/
/* @[declare_logging_generic] */
void IotLog_Generic( int libraryLogSetting,
const char * const pLibraryName,
int messageLevel,
const IotLogConfig_t * const pLogConfig,
const char * const pFormat,
... );
/* @[declare_logging_generic] */
/**
* @brief Generic function to log the contents of a buffer as bytes.
*
* This function is the generic buffer logging function shared across all libraries.
* The library-specific buffer logging function @ref logging_function_printbuffer is
* implemented using this function. Like @ref logging_function_printbuffer, this
* function is only available when @ref LIBRARY_LOG_LEVEL is #IOT_LOG_DEBUG.
*
* In most cases, the library-specific buffer logging function @ref
* logging_function_printbuffer should be called instead of this function.
*
* @param[in] pLibraryName The library name to print with the log. See @ref LIBRARY_LOG_NAME.
* @param[in] pHeader A message to print before printing the buffer.
* @param[in] pBuffer The buffer to print.
* @param[in] bufferSize The number of bytes in `pBuffer` to print.
*
* @return No return value. On errors, it prints nothing.
*
* @note To conserve memory, this function only allocates enough memory for a
* single line of output. Therefore, in multithreaded systems, its output may
* appear "fragmented" if other threads are logging simultaneously.
*/
/* @[declare_logging_genericprintbuffer] */
void IotLog_GenericPrintBuffer( const char * const pLibraryName,
const char * const pHeader,
const uint8_t * const pBuffer,
size_t bufferSize );
/* @[declare_logging_genericprintbuffer] */
#endif /* ifndef IOT_LOGGING_H_ */

View file

@ -0,0 +1,220 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_logging_setup.h
* @brief Defines the logging macro #IotLog.
*/
#ifndef IOT_LOGGING_SETUP_H_
#define IOT_LOGGING_SETUP_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Logging include. Because it's included here, iot_logging.h never needs
* to be included in source. */
#include "iot_logging.h"
/**
* @functionpage{IotLog,logging,log}
* @functionpage{IotLog_PrintBuffer,logging,printbuffer}
*/
/**
* @def IotLog( messageLevel, pLogConfig, ... )
* @brief Logging function for a specific library. In most cases, this is the
* logging function to call.
*
* This function prints a single log message. It is available when @ref
* LIBRARY_LOG_LEVEL is not #IOT_LOG_NONE. Log messages automatically
* include the [log level](@ref logging_constants_levels), [library name]
* (@ref LIBRARY_LOG_NAME), and time. An optional @ref IotLogConfig_t may
* be passed to this function to hide information for a single log message.
*
* The logging library must be set up before this function may be called. See
* @ref logging_setup_use for more information.
*
* This logging function also has the following abbreviated forms that can be used
* when an #IotLogConfig_t isn't needed.
*
* Name | Equivalent to
* ---- | -------------
* #IotLogError | @code{c} IotLog( IOT_LOG_ERROR, NULL, ... ) @endcode
* #IotLogWarn | @code{c} IotLog( IOT_LOG_WARN, NULL, ... ) @endcode
* #IotLogInfo | @code{c} IotLog( IOT_LOG_INFO, NULL, ... ) @endcode
* #IotLogDebug | @code{c} IotLog( IOT_LOG_DEBUG, NULL, ... ) @endcode
*
* @param[in] messageLevel Log level of this message. Must be one of the
* @ref logging_constants_levels.
* @param[in] pLogConfig Pointer to an #IotLogConfig_t. Optional; pass `NULL`
* to ignore.
* @param[in] ... Message and format specification.
*
* @return No return value. On errors, it prints nothing.
*
* @note This function may be implemented as a macro.
* @see @ref logging_function_generic for the generic (not library-specific)
* logging function.
*/
/**
* @def IotLog_PrintBuffer( pHeader, pBuffer, bufferSize )
* @brief Log the contents of buffer as bytes. Only available when @ref
* LIBRARY_LOG_LEVEL is #IOT_LOG_DEBUG.
*
* This function prints the bytes located at a given memory address. It is
* intended for debugging only, and is therefore only available when @ref
* LIBRARY_LOG_LEVEL is #IOT_LOG_DEBUG.
*
* Log messages printed by this function <b>always</b> include the [log level]
* (@ref logging_constants_levels), [library name](@ref LIBRARY_LOG_NAME),
* and time. In addition, this function may print an optional header `pHeader`
* before it prints the contents of the buffer. This function does not have an
* #IotLogConfig_t parameter.
*
* The logging library must be set up before this function may be called. See
* @ref logging_setup_use for more information.
*
* @param[in] pHeader A message to log before the buffer. Optional; pass `NULL`
* to ignore.
* @param[in] pBuffer Pointer to start of buffer.
* @param[in] bufferSize Size of `pBuffer`.
*
* @return No return value. On errors, it prints nothing.
*
* @note This function may be implemented as a macro.
* @note To conserve memory, @ref logging_function_genericprintbuffer (the underlying
* implementation) only allocates enough memory for a single line of output. Therefore,
* in multithreaded systems, its output may appear "fragmented" if other threads are
* logging simultaneously.
* @see @ref logging_function_genericprintbuffer for the generic (not library-specific)
* buffer logging function.
*
* <b>Example</b>
* @code{c}
* const uint8_t pBuffer[] = { 0x00, 0x01, 0x02, 0x03 };
*
* IotLog_PrintBuffer( "This buffer contains:",
* pBuffer,
* 4 );
* @endcode
* The code above prints something like the following:
* @code{c}
* [DEBUG][LIB_NAME][2018-01-01 12:00:00] This buffer contains:
* 00 01 02 03
* @endcode
*/
/**
* @def IotLogError( ... )
* @brief Abbreviated logging macro for level #IOT_LOG_ERROR.
*
* Equivalent to:
* @code{c}
* IotLog( IOT_LOG_ERROR, NULL, ... )
* @endcode
*/
/**
* @def IotLogWarn( ... )
* @brief Abbreviated logging macro for level #IOT_LOG_WARN.
*
* Equivalent to:
* @code{c}
* IotLog( IOT_LOG_WARN, NULL, ... )
* @endcode
*/
/**
* @def IotLogInfo( ... )
* @brief Abbreviated logging macro for level #IOT_LOG_INFO.
*
* Equivalent to:
* @code{c}
* IotLog( IOT_LOG_INFO, NULL, ... )
* @endcode
*/
/**
* @def IotLogDebug( ... )
* @brief Abbreviated logging macro for level #IOT_LOG_DEBUG.
*
* Equivalent to:
* @code{c}
* IotLog( IOT_LOG_DEBUG, NULL, ... )
* @endcode
*/
/* Check that LIBRARY_LOG_LEVEL is defined and has a valid value. */
#if !defined( LIBRARY_LOG_LEVEL ) || \
( LIBRARY_LOG_LEVEL != IOT_LOG_NONE && \
LIBRARY_LOG_LEVEL != IOT_LOG_ERROR && \
LIBRARY_LOG_LEVEL != IOT_LOG_WARN && \
LIBRARY_LOG_LEVEL != IOT_LOG_INFO && \
LIBRARY_LOG_LEVEL != IOT_LOG_DEBUG )
#error "Please define LIBRARY_LOG_LEVEL as either IOT_LOG_NONE, IOT_LOG_ERROR, IOT_LOG_WARN, IOT_LOG_INFO, or IOT_LOG_DEBUG."
/* Check that LIBRARY_LOG_NAME is defined and has a valid value. */
#elif !defined( LIBRARY_LOG_NAME )
#error "Please define LIBRARY_LOG_NAME."
#else
/* Define IotLog if the log level is greater than "none". */
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
#define IotLog( messageLevel, pLogConfig, ... ) \
IotLog_Generic( LIBRARY_LOG_LEVEL, \
LIBRARY_LOG_NAME, \
messageLevel, \
pLogConfig, \
__VA_ARGS__ )
/* Define the abbreviated logging macros. */
#define IotLogError( ... ) IotLog( IOT_LOG_ERROR, NULL, __VA_ARGS__ )
#define IotLogWarn( ... ) IotLog( IOT_LOG_WARN, NULL, __VA_ARGS__ )
#define IotLogInfo( ... ) IotLog( IOT_LOG_INFO, NULL, __VA_ARGS__ )
#define IotLogDebug( ... ) IotLog( IOT_LOG_DEBUG, NULL, __VA_ARGS__ )
/* If log level is DEBUG, enable the function to print buffers. */
#if LIBRARY_LOG_LEVEL >= IOT_LOG_DEBUG
#define IotLog_PrintBuffer( pHeader, pBuffer, bufferSize ) \
IotLog_GenericPrintBuffer( LIBRARY_LOG_NAME, \
pHeader, \
pBuffer, \
bufferSize )
#else
#define IotLog_PrintBuffer( pHeader, pBuffer, bufferSize )
#endif
/* Remove references to IotLog from the source code if logging is disabled. */
#else
/* @[declare_logging_log] */
#define IotLog( messageLevel, pLogConfig, ... )
/* @[declare_logging_log] */
/* @[declare_logging_printbuffer] */
#define IotLog_PrintBuffer( pHeader, pBuffer, bufferSize )
/* @[declare_logging_printbuffer] */
#define IotLogError( ... )
#define IotLogWarn( ... )
#define IotLogInfo( ... )
#define IotLogDebug( ... )
#endif
#endif
#endif /* ifndef IOT_LOGGING_SETUP_H_ */

View file

@ -0,0 +1,197 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_static_memory.h
* @brief Common functions for managing static buffers. Only used when
* @ref IOT_STATIC_MEMORY_ONLY is `1`.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* The functions in this file should only exist in static memory only mode, hence
* the check for IOT_STATIC_MEMORY_ONLY in the double inclusion guard. */
#if !defined( IOT_STATIC_MEMORY_H_ ) && ( IOT_STATIC_MEMORY_ONLY == 1 )
#define IOT_STATIC_MEMORY_H_
/* Standard includes. */
#include <stddef.h>
#include <stdint.h>
/**
* @functionspage{static_memory,static memory component}
* - @functionname{static_memory_function_findfree}
* - @functionname{static_memory_function_returninuse}
* - @functionname{static_memory_function_messagebuffersize}
* - @functionname{static_memory_function_mallocmessagebuffer}
* - @functionname{static_memory_function_freemessagebuffer}
*/
/*------------------------- Buffer allocation and free ----------------------*/
/**
* @functionpage{IotStaticMemory_FindFree,static_memory,findfree}
* @functionpage{IotStaticMemory_ReturnInUse,static_memory,returninuse}
*/
/**
* @brief Find a free buffer using the "in-use" flags.
*
* If a free buffer is found, this function marks the buffer in-use. This function
* is common to the static memory implementation.
*
* @param[in] pInUse The "in-use" flags to search.
* @param[in] limit How many flags to check, i.e. the size of `pInUse`.
*
* @return The index of a free buffer; `-1` if no free buffers are available.
*
* <b>Example</b>:
* @code{c}
* // To use this function, first declare two arrays. One provides the statically-allocated
* // objects, the other provides flags to determine which objects are in-use.
* #define NUMBER_OF_OBJECTS ...
* #define OBJECT_SIZE ...
* static uint32_t _pInUseObjects[ NUMBER_OF_OBJECTS ] = { 0 };
* static uint8_t _pObjects[ NUMBER_OF_OBJECTS ][ OBJECT_SIZE ] = { { 0 } }; // Placeholder for objects.
*
* // The function to statically allocate objects. Must have the same signature
* // as malloc().
* void * Iot_MallocObject( size_t size )
* {
* int32_t freeIndex = -1;
* void * pNewObject = NULL;
*
* // Check that sizes match.
* if( size != OBJECT_SIZE )
* {
* // Get the index of a free object.
* freeIndex = IotStaticMemory_FindFree( _pInUseMessageBuffers,
* IOT_MESSAGE_BUFFERS );
*
* if( freeIndex != -1 )
* {
* pNewBuffer = &( _pMessageBuffers[ freeIndex ][ 0 ] );
* }
* }
*
* return pNewBuffer;
* }
* @endcode
*/
/* @[declare_static_memory_findfree] */
int32_t IotStaticMemory_FindFree( uint32_t * pInUse,
size_t limit );
/* @[declare_static_memory_findfree] */
/**
* @brief Return an "in-use" buffer.
*
* This function is common to the static memory implementation.
*
* @param[in] ptr Pointer to the buffer to return.
* @param[in] pPool The pool of buffers that the in-use buffer was allocated from.
* @param[in] pInUse The "in-use" flags for pPool.
* @param[in] limit How many buffers (and flags) to check while searching for ptr.
* @param[in] elementSize The size of a single element in pPool.
*
* <b>Example</b>:
* @code{c}
* // To use this function, first declare two arrays. One provides the statically-allocated
* // objects, the other provides flags to determine which objects are in-use.
* #define NUMBER_OF_OBJECTS ...
* #define OBJECT_SIZE ...
* static uint32_t _pInUseObjects[ NUMBER_OF_OBJECTS ] = { 0 };
* static uint8_t _pObjects[ NUMBER_OF_OBJECTS ][ OBJECT_SIZE ] = { { 0 } }; // Placeholder for objects.
*
* // The function to free statically-allocated objects. Must have the same signature
* // as free().
* void Iot_FreeObject( void * ptr )
* {
* IotStaticMemory_ReturnInUse( ptr,
* _pObjects,
* _pInUseObjects,
* NUMBER_OF_OBJECTS,
* OBJECT_SIZE );
* }
* @endcode
*/
/* @[declare_static_memory_returninuse] */
void IotStaticMemory_ReturnInUse( void * ptr,
void * pPool,
uint32_t * pInUse,
size_t limit,
size_t elementSize );
/* @[declare_static_memory_returninuse] */
/*------------------------ Message buffer management ------------------------*/
/**
* @functionpage{Iot_MessageBufferSize,static_memory,messagebuffersize}
* @functionpage{Iot_MallocMessageBuffer,static_memory,mallocmessagebuffer}
* @functionpage{Iot_FreeMessageBuffer,static_memory,freemessagebuffer}
*/
/**
* @brief Get the fixed size of a message buffer.
*
* The size of the message buffers are known at compile time, but it is a [constant]
* (@ref IOT_MESSAGE_BUFFER_SIZE) that may not be visible to all source files.
* This function allows other source files to know the size of a message buffer.
*
* @return The size, in bytes, of a single message buffer.
*/
/* @[declare_static_memory_messagebuffersize] */
size_t Iot_MessageBufferSize( void );
/* @[declare_static_memory_messagebuffersize] */
/**
* @brief Get an empty message buffer.
*
* This function is the analog of [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html)
* for message buffers.
*
* @param[in] size Requested size for a message buffer.
*
* @return Pointer to the start of a message buffer. If the `size` argument is larger
* than the [fixed size of a message buffer](@ref IOT_MESSAGE_BUFFER_SIZE)
* or no message buffers are available, `NULL` is returned.
*/
/* @[declare_static_memory_mallocmessagebuffer] */
void * Iot_MallocMessageBuffer( size_t size );
/* @[declare_static_memory_mallocmessagebuffer] */
/**
* @brief Free an in-use message buffer.
*
* This function is the analog of [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html)
* for message buffers.
*
* @param[in] ptr Pointer to the message buffer to free.
*/
/* @[declare_static_memory_freemessagebuffer] */
void Iot_FreeMessageBuffer( void * ptr );
/* @[declare_static_memory_freemessagebuffer] */
#endif /* if !defined( IOT_STATIC_MEMORY_H_ ) && ( IOT_STATIC_MEMORY_ONLY == 1 ) */

View file

@ -0,0 +1,451 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_logging.c
* @brief Implementation of logging functions from iot_logging.h
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
/* Platform clock include. */
#include "platform/iot_clock.h"
/* Logging includes. */
#include "iot_logging.h"
/*-----------------------------------------------------------*/
/* This implementation assumes the following values for the log level constants.
* Ensure that the values have not been modified. */
#if IOT_LOG_NONE != 0
#error "IOT_LOG_NONE must be 0."
#endif
#if IOT_LOG_ERROR != 1
#error "IOT_LOG_ERROR must be 1."
#endif
#if IOT_LOG_WARN != 2
#error "IOT_LOG_WARN must be 2."
#endif
#if IOT_LOG_INFO != 3
#error "IOT_LOG_INFO must be 3."
#endif
#if IOT_LOG_DEBUG != 4
#error "IOT_LOG_DEBUG must be 4."
#endif
/**
* @def IotLogging_Puts( message )
* @brief Function the logging library uses to print a line.
*
* This function can be set by using a define. By default, the standard library
* [puts](http://pubs.opengroup.org/onlinepubs/9699919799/functions/puts.html)
* function is used.
*/
#ifndef IotLogging_Puts
#define IotLogging_Puts puts
#endif
/*
* Provide default values for undefined memory allocation functions based on
* the usage of dynamic memory allocation.
*/
#if IOT_STATIC_MEMORY_ONLY == 1
/* Static memory allocation header. */
#include "iot_static_memory.h"
/**
* @brief Allocate a new logging buffer. This function must have the same
* signature as [malloc](http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
#ifndef IotLogging_Malloc
#define IotLogging_Malloc Iot_MallocMessageBuffer
#endif
/**
* @brief Free a logging buffer. This function must have the same signature
* as [free](http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
#ifndef IotLogging_Free
#define IotLogging_Free Iot_FreeMessageBuffer
#endif
/**
* @brief Get the size of a logging buffer. Statically-allocated buffers
* should all have the same size.
*/
#ifndef IotLogging_StaticBufferSize
#define IotLogging_StaticBufferSize Iot_MessageBufferSize
#endif
#else /* if IOT_STATIC_MEMORY_ONLY == 1 */
#ifndef IotLogging_Malloc
#include <stdlib.h>
#define IotLogging_Malloc malloc
#endif
#ifndef IotLogging_Free
#include <stdlib.h>
#define IotLogging_Free free
#endif
#endif /* if IOT_STATIC_MEMORY_ONLY == 1 */
/**
* @brief A guess of the maximum length of a timestring.
*
* There's no way for this logging library to know the length of a timestring
* before it's generated. Therefore, the logging library will assume a maximum
* length of any timestring it may get. This value should be generous enough
* to accommodate the vast majority of timestrings.
*
* @see @ref platform_clock_function_gettimestring
*/
#define MAX_TIMESTRING_LENGTH ( 64 )
/**
* @brief The longest string in #_pLogLevelStrings (below), plus 3 to accommodate
* `[]` and a null-terminator.
*/
#define MAX_LOG_LEVEL_LENGTH ( 8 )
/**
* @brief How many bytes @ref logging_function_genericprintbuffer should output on
* each line.
*/
#define BYTES_PER_LINE ( 16 )
/*-----------------------------------------------------------*/
/**
* @brief Lookup table for log levels.
*
* Converts one of the @ref logging_constants_levels to a string.
*/
static const char * const _pLogLevelStrings[ 5 ] =
{
"", /* IOT_LOG_NONE */
"ERROR", /* IOT_LOG_ERROR */
"WARN ", /* IOT_LOG_WARN */
"INFO ", /* IOT_LOG_INFO */
"DEBUG" /* IOT_LOG_DEBUG */
};
/*-----------------------------------------------------------*/
#if !defined( IOT_STATIC_MEMORY_ONLY ) || ( IOT_STATIC_MEMORY_ONLY == 0 )
static bool _reallocLoggingBuffer( void ** pOldBuffer,
size_t newSize,
size_t oldSize )
{
bool status = false;
/* Allocate a new, larger buffer. */
void * pNewBuffer = IotLogging_Malloc( newSize );
/* Ensure that memory allocation succeeded. */
if( pNewBuffer != NULL )
{
/* Copy the data from the old buffer to the new buffer. */
( void ) memcpy( pNewBuffer, *pOldBuffer, oldSize );
/* Free the old buffer and update the pointer. */
IotLogging_Free( *pOldBuffer );
*pOldBuffer = pNewBuffer;
status = true;
}
return status;
}
#endif /* if !defined( IOT_STATIC_MEMORY_ONLY ) || ( IOT_STATIC_MEMORY_ONLY == 0 ) */
/*-----------------------------------------------------------*/
void IotLog_Generic( int libraryLogSetting,
const char * const pLibraryName,
int messageLevel,
const IotLogConfig_t * const pLogConfig,
const char * const pFormat,
... )
{
int requiredMessageSize = 0;
size_t bufferSize = 0,
bufferPosition = 0, timestringLength = 0;
char * pLoggingBuffer = NULL;
va_list args;
/* If the library's log level setting is lower than the message level,
* return without doing anything. */
if( ( messageLevel == 0 ) || ( messageLevel > libraryLogSetting ) )
{
return;
}
if( ( pLogConfig == NULL ) || ( pLogConfig->hideLogLevel == false ) )
{
/* Add length of log level if requested. */
bufferSize += MAX_LOG_LEVEL_LENGTH;
}
/* Estimate the amount of buffer needed for this log message. */
if( ( pLogConfig == NULL ) || ( pLogConfig->hideLibraryName == false ) )
{
/* Add size of library name if requested. Add 2 to accommodate "[]". */
bufferSize += strlen( pLibraryName ) + 2;
}
if( ( pLogConfig == NULL ) || ( pLogConfig->hideTimestring == false ) )
{
/* Add length of timestring if requested. */
bufferSize += MAX_TIMESTRING_LENGTH;
}
/* Add 64 as an initial (arbitrary) guess for the length of the message. */
bufferSize += 64;
/* In static memory mode, check that the log message will fit in the a
* static buffer. */
#if IOT_STATIC_MEMORY_ONLY == 1
if( bufferSize >= IotLogging_StaticBufferSize() )
{
/* If the static buffers are likely too small to fit the log message,
* return. */
return;
}
/* Otherwise, update the buffer size to the size of a static buffer. */
bufferSize = IotLogging_StaticBufferSize();
#endif
/* Allocate memory for the logging buffer. */
pLoggingBuffer = ( char * ) IotLogging_Malloc( bufferSize );
if( pLoggingBuffer == NULL )
{
return;
}
/* Print the message log level if requested. */
if( ( pLogConfig == NULL ) || ( pLogConfig->hideLogLevel == false ) )
{
/* Ensure that message level is valid. */
if( ( messageLevel >= IOT_LOG_NONE ) && ( messageLevel <= IOT_LOG_DEBUG ) )
{
/* Add the log level string to the logging buffer. */
requiredMessageSize = snprintf( pLoggingBuffer + bufferPosition,
bufferSize - bufferPosition,
"[%s]",
_pLogLevelStrings[ messageLevel ] );
/* Check for encoding errors. */
if( requiredMessageSize <= 0 )
{
IotLogging_Free( pLoggingBuffer );
return;
}
/* Update the buffer position. */
bufferPosition += ( size_t ) requiredMessageSize;
}
}
/* Print the library name if requested. */
if( ( pLogConfig == NULL ) || ( pLogConfig->hideLibraryName == false ) )
{
/* Add the library name to the logging buffer. */
requiredMessageSize = snprintf( pLoggingBuffer + bufferPosition,
bufferSize - bufferPosition,
"[%s]",
pLibraryName );
/* Check for encoding errors. */
if( requiredMessageSize <= 0 )
{
IotLogging_Free( pLoggingBuffer );
return;
}
/* Update the buffer position. */
bufferPosition += ( size_t ) requiredMessageSize;
}
/* Print the timestring if requested. */
if( ( pLogConfig == NULL ) || ( pLogConfig->hideTimestring == false ) )
{
/* Add the opening '[' enclosing the timestring. */
pLoggingBuffer[ bufferPosition ] = '[';
bufferPosition++;
/* Generate the timestring and add it to the buffer. */
if( IotClock_GetTimestring( pLoggingBuffer + bufferPosition,
bufferSize - bufferPosition,
&timestringLength ) == true )
{
/* If the timestring was successfully generated, add the closing "]". */
bufferPosition += timestringLength;
pLoggingBuffer[ bufferPosition ] = ']';
bufferPosition++;
}
else
{
/* Sufficient memory for a timestring should have been allocated. A timestring
* probably failed to generate due to a clock read error; remove the opening '['
* from the logging buffer. */
bufferPosition--;
pLoggingBuffer[ bufferPosition ] = '\0';
}
}
/* Add a padding space between the last closing ']' and the message, unless
* the logging buffer is empty. */
if( bufferPosition > 0 )
{
pLoggingBuffer[ bufferPosition ] = ' ';
bufferPosition++;
}
va_start( args, pFormat );
/* Add the log message to the logging buffer. */
requiredMessageSize = vsnprintf( pLoggingBuffer + bufferPosition,
bufferSize - bufferPosition,
pFormat,
args );
va_end( args );
/* If the logging buffer was too small to fit the log message, reallocate
* a larger logging buffer. */
if( ( size_t ) requiredMessageSize >= bufferSize - bufferPosition )
{
#if IOT_STATIC_MEMORY_ONLY == 1
/* There's no point trying to allocate a larger static buffer. Return
* immediately. */
IotLogging_Free( pLoggingBuffer );
return;
#else
if( _reallocLoggingBuffer( ( void ** ) &pLoggingBuffer,
( size_t ) requiredMessageSize + bufferPosition + 1,
bufferSize ) == false )
{
/* If buffer reallocation failed, return. */
IotLogging_Free( pLoggingBuffer );
return;
}
/* Reallocation successful, update buffer size. */
bufferSize = ( size_t ) requiredMessageSize + bufferPosition + 1;
/* Add the log message to the buffer. Now that the buffer has been
* reallocated, this should succeed. */
va_start( args, pFormat );
requiredMessageSize = vsnprintf( pLoggingBuffer + bufferPosition,
bufferSize - bufferPosition,
pFormat,
args );
va_end( args );
#endif /* if IOT_STATIC_MEMORY_ONLY == 1 */
}
/* Check for encoding errors. */
if( requiredMessageSize <= 0 )
{
IotLogging_Free( pLoggingBuffer );
return;
}
/* Print the logging buffer to stdout. */
IotLogging_Puts( pLoggingBuffer );
/* Free the logging buffer. */
IotLogging_Free( pLoggingBuffer );
}
/*-----------------------------------------------------------*/
void IotLog_GenericPrintBuffer( const char * const pLibraryName,
const char * const pHeader,
const uint8_t * const pBuffer,
size_t bufferSize )
{
size_t i = 0, offset = 0;
/* Allocate memory to hold each line of the log message. Since each byte
* of pBuffer is printed in 4 characters (2 digits, a space, and a null-
* terminator), the size of each line is 4 * BYTES_PER_LINE. */
char * pMessageBuffer = IotLogging_Malloc( 4 * BYTES_PER_LINE );
/* Exit if no memory is available. */
if( pMessageBuffer == NULL )
{
return;
}
/* Print pHeader before printing pBuffer. */
if( pHeader != NULL )
{
IotLog_Generic( IOT_LOG_DEBUG,
pLibraryName,
IOT_LOG_DEBUG,
NULL,
pHeader );
}
/* Print each byte in pBuffer. */
for( i = 0; i < bufferSize; i++ )
{
/* Print a line if BYTES_PER_LINE is reached. But don't print a line
* at the beginning (when i=0). */
if( ( i % BYTES_PER_LINE == 0 ) && ( i != 0 ) )
{
IotLogging_Puts( pMessageBuffer );
/* Reset offset so that pMessageBuffer is filled from the beginning. */
offset = 0;
}
/* Print a single byte into pMessageBuffer. */
( void ) snprintf( pMessageBuffer + offset, 4, "%02x ", pBuffer[ i ] );
/* Move the offset where the next character is printed. */
offset += 3;
}
/* Print the final line of bytes. This line isn't printed by the for-loop above. */
IotLogging_Puts( pMessageBuffer );
/* Free memory used by this function. */
IotLogging_Free( pMessageBuffer );
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,10 @@
+ mqtt
Contains the implementation of the MQTT library.
+ https
Contains the implementation of the HTTPS Client library.
+ common
Contains the implementation of utility functions used by other IoT libraries.
Further libraries will be rolled out soon.

View file

@ -0,0 +1,852 @@
/*
* Amazon FreeRTOS HTTPS Client V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_https_client.h
* @brief User-facing functions of the Amazon FreeRTOS HTTPS Client library.
*/
#ifndef IOT_HTTPS_CLIENT_H_
#define IOT_HTTPS_CLIENT_H_
/* The config header is always included first. */
#include "iot_config.h"
/* HTTP types include. */
#include "types/iot_https_types.h"
/**
* @functionspage{https_client,HTTPS Client Library}
* - @functionname{https_client_function_init}
* - @functionname{https_client_function_deinit}
* - @functionname{https_client_function_disconnect}
* - @functionname{https_client_function_connect}
* - @functionname{https_client_function_initializerequest}
* - @functionname{https_client_function_addheader}
* - @functionname{https_client_function_writerequestbody}
* - @functionname{https_client_function_sendsync}
* - @functionname{https_client_function_sendasync}
* - @functionname{https_client_function_cancelrequestasync}
* - @functionname{https_client_function_cancelresponseasync}
* - @functionname{https_client_function_readresponsestatus}
* - @functionname{https_client_function_readcontentlength}
* - @functionname{https_client_function_readheader}
* - @functionname{https_client_function_readresponsebody}
*/
/**
* @functionpage{IotHttpsClient_Init,https_client,init}
* @functionpage{IotHttpsClient_Deinit,https_client,deinit}
* @functionpage{IotHttpsClient_Disconnect,https_client,disconnect}
* @functionpage{IotHttpsClient_Connect,https_client,connect}
* @functionpage{IotHttpsClient_InitializeRequest,https_client,initializerequest}
* @functionpage{IotHttpsClient_AddHeader,https_client,addheader}
* @functionpage{IotHttpsClient_WriteRequestBody,https_client,writerequestbody}
* @functionpage{IotHttpsClient_SendSync,https_client,sendsync}
* @functionpage{IotHttpsClient_SendAsync,https_client,sendasync}
* @functionpage{IotHttpsClient_CancelRequestAsync,https_client,cancelrequestasync}
* @functionpage{IotHttpsClient_CancelResponseAsync,https_client,cancelresponseasync}
* @functionpage{IotHttpsClient_ReadResponseStatus,https_client,readresponsestatus}
* @functionpage{IotHttpsClient_ReadContentLength,https_client,readcontentlength}
* @functionpage{IotHttpsClient_ReadHeader,https_client,readheader}
* @functionpage{IotHttpsClient_ReadResponseBody,https_client,readresponsebody}
*/
/**
* @brief One-time initialization of the IoT HTTPS Client library.
*
* <b>This must be called once before calling any API.</b>
*
* @warning No thread safety guarantees are provided for this function.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the HTTPS library is successfully initialized.
*
* @see @ref https_client_function_cleanup
*/
/* @[declare_https_client_init] */
IotHttpsReturnCode_t IotHttpsClient_Init( void );
/* @[declare_https_client_init] */
/**
* @brief One time clean up of the IoT HTTPS Client library.
*
* This function frees resources taken in in @ref https_client_function_init. It should be called after
* all HTTPS Connections have been close. HTTPS Connections are represented by #IotHttpsConnectionHandle_t and returned
* by @ref https_client_function_connect. After this function returns @ref https_client_function_init
* must be called again to use this library.
*
* @warning No thread safety guarantees are provided for this function.
*/
/* @[declare_https_client_cleanup] */
void IotHttpsClient_Cleanup( void );
/* @[declare_https_client_cleanup] */
/**
* @cond DOXYGEN_IGNORE
*
* Backward compatibility function for one time clean up of the IoT HTTPS Client library.
*/
#define IotHttpsClient_Deinit IotHttpsClient_Cleanup
/** @endcond */
/**
* @brief Explicitly connect to the HTTPS server given the connection configuration pConnConfig.
*
* This routine blocks until the connection is complete.
*
* This function opens a new HTTPS connection with the server specified in #IotHttpsConnectionInfo_t.pAddress. The
* connection is established by default on top of TLS over TCP. If the application wants to connect over TCP only, then
* it must add the @ref IOT_HTTPS_IS_NON_TLS_FLAG to #IotHttpsConnectionInfo_t.flags. This is done at the application's
* own risk.
*
* When the the last HTTP request sent on the connection is specified as persistent and we want to close the connection,
* @ref https_client_function_disconnect must always be called on the valid #IotHttpsConnectionHandle_t. For more
* information about persistent HTTP connections please see #IotHttpsRequestInfo_t.isNonPersistent.
*
* If the application receives a #IOT_HTTPS_NETWORK_ERROR from @ref https_client_function_sendsync or
* @ref https_client_function_sendasync, on a persistent request, then the connection will be closed. The application
* can call this this function again to reestablish the connection.
*
* If pConnHandle passed in is valid and represents a previously opened connection, this function will disconnect,
* then reconnect. Before calling this function make sure that all outstanding requests on the connection have
* completed. Outstanding requests are completed when @ref https_client_function_sendsync has returned or when
* #IotHttpsClientCallbacks_t.responseCompleteCallback has been invoked for requests scheduled with
* @ref https_client_function_sendasync.
*
* Keep in mind that many HTTP servers will close a connection, if it does not receive any requests, after a certain
* amount of time. Many web servers may close the connection after 30-60 seconds. The state of pConnHandle will still be
* in a connected state if this happens. If the server closed the connection, then the next request on the connection
* will fail to send with a network error and the connection will move to a closed state.
*
* Also keep in mind that some HTTP servers do not accept persistent requests. Some HTTP servers will ignore that the
* request contains the "Connection: keep-alive" header and close the connection immediately after sending the response.
* If this happens, then the next request on the connection will fail to send with a network error and the connection
* will close.
*
* To know if the connection was closed by the server, debug logging can be turned on to view the network error code
* received. Debug logging is configured when @ref IOT_LOG_LEVEL_HTTPS is set to @ref IOT_LOG_DEBUG in iot_config.h.
*
* #IotHttpsConnectionInfo_t.userBuffer is used to store the internal context and therefore, multiple threads
* calling this function simultaneously must ensure to use different #IotHttpsConnectionInfo_t objects.
*
* See @ref connectionUserBufferMinimumSize for information about the user buffer configured in
* #IotHttpsConnectionInfo_t.userBuffer needed to create a valid connection handle.
*
* @param[out] pConnHandle - Handle returned representing the open connection. NULL if the function failed.
* @param[in] pConnInfo - Configurations for the HTTPS connection.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the connection was successful.
* - #IOT_HTTPS_CONNECTION_ERROR if the connection failed.
* - #IOT_HTTPS_INVALID_PARAMETER if NULL parameters were passed in.
* - #IOT_HTTPS_INTERNAL_ERROR if there was an error creating resources for the connection context.
*
* <b>Example</b>
* @code{c}
* // An initialized network interface.
* IotNetworkInterface_t* pNetworkInterface;
*
* // Parameters to HTTPS Client connect.
* IotHttpsConnectionInfo_t connInfo = IOT_HTTPS_CONNECTION_INFO_INITIALIZER;
* IotHttpsConnectionHandle_t connHandle = IOT_HTTPS_CONNECTION_HANDLE_INITIALIZER;
* uint8_t* pConnUserBuffer = (uint8_t*)malloc(connectionUserBufferMinimumSize);
*
* // Set the connection configuration information.
* connInfo.pAddress = "www.amazon.com";
* connInfo.addressLen = strlen("www.amazon.com");
* connInfo.port = 443;
* connInfo.flags = 0;
* connInfo.pAlpnProtocols = "alpnproto0,alpnproto1"
* connInfo.pCaCert = HTTPS_TRUSTED_ROOT_CA; // defined elsewhere
* connInfo.caCertLen = sizeof( HTTPS_TRUSTED_ROOT_CA );
* connInfo.userBuffer.pBuffer = pConnUserBuffer;
* connInfo.userBuffer.bufferLen = connectionUserBufferMinimumSize;
* connInfo.pClientCert = TLS_CLIENT_CERT;
* connInfo.clientCertLen = sizeof( TLS_CLIENT_CERT );
* connInfo.pPrivateKey = TLS_CLIENT_PRIV_KEY;
* connInfo.privateKeyLen = sizeof( TLS_CLIENT_PRIV_KEY );
* connInfo.pNetworkInterface = pNetworkInterface;
*
* IotHttpsReturnCode_t returnCode = IotHttpsClient_Connect(&connHandle, &connInfo);
* if( returnCode == IOT_HTTPS_OK )
* {
* // Do something with the HTTPS connection...
*
* // Clean up and close the HTTPS connection once it's no longer needed.
* IotHttpsClient_Disconnect(connHandle);
* }
* @endcode
*/
/* @[declare_https_client_connect] */
IotHttpsReturnCode_t IotHttpsClient_Connect( IotHttpsConnectionHandle_t * pConnHandle,
IotHttpsConnectionInfo_t * pConnInfo );
/* @[declare_https_client_connect] */
/**
* @brief Disconnect from the HTTPS server given the connection handle connHandle.
*
* This routine blocks until the disconnect is complete.
* If the connection handle is not valid, the behavior is undefined.
* If the connection handle is already disconnected then this routine will return IOT_HTTPS_OK.
*
* When the HTTP request is specified as persistent and we want to close the connection, this API must always
* be called on the valid #IotHttpsConnectionHandle_t. For more information about persistent HTTP connections please see
* #IotHttpsRequestInfo_t.isNonPersistent.
*
* When the HTTP request is specified as non-persistent, by setting #IotHttpsRequestInfo_t.isNonPersistent to true, then
* this function will be called automatically on the valid IotHttpsConnectionHandle_t after receiving the response. There
* is no need to call this function in case of a non-persistent request.
*
* This will put the internal connection state in #IotHttpsConnectionHandle_t to disconnected.
*
* If the application receives a #IOT_HTTPS_NETWORK_ERROR from @ref https_client_function_sendsync or
* @ref https_client_function_sendasync, on a persistent request, that does not always mean the connection has been
* disconnected. This function MUST be called to close the connection and clean up connection resources taken by
* #IotHttpsConnectionHandle_t.
*
* This function will cancel all pending requests on the connection. If a request currently being sent on the connection,
* then this function will disconnect the connection, but it will not free network connection resource and will return
* with #IOT_HTTPS_BUSY. The application may call this function again later to try again.
*
* Multiple threads must not call this function for the same #IotHttpsConnectionHandle_t. Multiple threads
* can call this function for different #IotHttpsConnectionHandle_t. Make sure that all request/responses
* have finished on the connection before calling this API. Outstanding requests are completed when
* @ref https_client_function_sendsync has returned or when #IotHttpsClientCallbacks_t.responseCompleteCallback
* has been invoked for requests scheduled with @ref https_client_function_sendasync.
*
* @param[in] connHandle - Valid handle representing an open connection.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the disconnect was successful
* - #IOT_HTTPS_INVALID_PARAMETER if NULL parameters were passed in.
* - #IOT_HTTPS_BUSY if the connection is in use and cannot be destroyed.
*/
/* @[declare_https_client_disconnect] */
IotHttpsReturnCode_t IotHttpsClient_Disconnect( IotHttpsConnectionHandle_t connHandle );
/* @[declare_https_client_disconnect] */
/**
* @brief Initializes the request by adding a formatted Request-Line to the start of HTTPS request header buffer.
*
* This function will initialize the HTTP request context by setting where to write the next headers to the start
* of the configured header buffer in #IotHttpsRequestInfo_t.userBuffer.
*
* The Request-Line will be added to the start of the headers space in #IotHttpsRequestInfo_t.userBuffer.
* The header space follows the request context in the user buffer. See @ref requestUserBufferMinimumSize for more
* information on sizing the #IotHttpsRequestInfo_t.userBuffer so that this function does not fail.
*
* The Request-Line generated is of the following format:
*
* @code
* method path version\r\n
* @endcode
*
* Example:
*
* @code
* GET /path/to/item.file?possible_query HTTP/1.1\r\n
* @endcode
*
* The initial required headers are also added to the #IotHttpsRequestInfo_t.userBuffer. These headers are User-Agent
* and Host. The User-Agent value is configured in iot_config.h using IOT_HTTPS_USER_AGENT. The Host value is the DNS
* resolvable server address.
*
* @param[out] pReqHandle - request handle representing the internal request context is returned. NULL if the function failed.
* @param[in] pReqInfo - HTTPS request information.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the Request-Line was successfully added to the header space in #IotHttpsRequestInfo_t.userBuffer.
* - #IOT_HTTPS_INSUFFICIENT_MEMORY if the Request-Line generated exceeds #IotHttpsUserBuffer_t.bufferLen in #IotHttpsRequestInfo_t.userBuffer.
* - #IOT_HTTPS_INVALID_PARAMETER for NULL parameters.
* - #IOT_HTTPS_INTERNAL_ERROR for library internal errors.
*
* <b>Example</b>
* @code{c}
* // An initialized network interface.
* IotNetworkInterface_t* pNetworkInterface;
*
* // Parameters to HTTPS Client request initialization.
* IotHttpsRequestInfo_t reqInfo = IOT_HTTPS_REQUEST_INFO_INITIALIZER;
* IotHttpsRequestHandle_t reqHandle = IOT_HTTPS_REQUEST_HANDLE_INITIALIZER;
* IotHttpsSyncInfo_t syncInfo = IOT_HTTPS_SYNC_INFO_INITIALIZER;
* // Leave some room for extra headers.
* uint32_t userBufferSize = requestUserBufferMinimumSize + 256;
* uint8_t* pRequestUserBuffer = (uint8_t*)malloc(userBufferSize);
*
* // Set the synchronous information.
* syncInfo.pBody = PREDEFINED_BODY_BUFFER;
* syncInfo.bodyLen = PREDEFINED_BODY_BUFFER_LEN;
*
* // Set the request configuration information.
* reqInfo.pPath = "/path_to_item?query_maybe";
* reqInfo.pPathLen = strlen("/path_to_item?query_maybe");
* reqInfo.method = IOT_HTTPS_METHOD_GET;
* reqInfo.pHost = "www.amazon.com";
* reqInfo.hostLen = strlen("www.amazon.com");
* reqInfo.isNonPersistent = false;
* reqInfo.userBuffer.pBuffer = pRequestUserBuffer;
* reqInfo.userBuffer.bufferLen = userBufferSize;
* reqInfo.isAsync = false;
* reqInfo.pSyncInfo = &syncInfo;
*
* IotHttpsReturnCode_t returnCode = IotHttpsClient_InitializeRequest(&reqHandle, &reqInfo);
* if( returnCode == IOT_HTTPS_OK )
* {
* // Handle the error.
* }
* @endcode
*/
/* @[declare_https_client_initializerequest] */
IotHttpsReturnCode_t IotHttpsClient_InitializeRequest( IotHttpsRequestHandle_t * pReqHandle,
IotHttpsRequestInfo_t * pReqInfo );
/* @[declare_https_client_initializerequest] */
/**
* @brief Add a header to the current HTTPS request represented by reqHandle.
*
* The header line is appended to the request header buffer space in #IotHttpsRequestInfo_t.userBuffer.
* Please see #requestUserBufferMinimumSize for information about sizing the #IotHttpsRequestInfo_t.userBuffer so
* that this function does not fail.
*
* Header lines are appended in the following format:
* @code
* header_field_name: header_value\r\n"
* @endcode
* Example:
* @code
* Range: bytes=1024-2047\r\n
* @endcode
* The last header line must be followed by a "\r\n" to separate the last header line from
* the entity body. These 2 characters are accounted for in #requestUserBufferMinimumSize.
*
* The remaining length, after the header is added, is printed to the system configured standard debug output when
* @ref IOT_LOG_LEVEL_HTTPS is set to @ref IOT_LOG_DEBUG in iot_config.h.
*
* For an asynchronous request, this function can be invoked before the request is sent with
* @ref https_client_function_sendasync, or during #IotHttpsClientCallbacks_t.appendHeaderCallback. It is
* recommended to invoke this function in #IotHttpsClientCallbacks_t.appendHeaderCallback.
*
* <b> Asynchronous Example </b>
* @code{c}
* void _applicationDefined_appendHeaderCallback(void * pPrivData, IotHttpsRequestHandle_t reqHandle)
* {
* ...
* char date_in_iso8601[17] = { 0 };
* GET_DATE_IN_ISO8601(date_in_iso8601);
* const char amz_date_header[] = "x-amz-date";
* uint32_t amz_date_header_length = strlen(amz_date_header);
* IotHttpsClient_AddHeader(reqHandle, amz_date_header, amz_date_header_length, date_in_iso8601, strlen(date_in_iso8601));
* ...
* }
* @endcode
*
* For a synchronous request, if extra headers are desired to be added, this function must be invoked before
* @ref https_client_function_sendsync.
* <b> Synchronous Example </b>
* @code{c}
* ...
* char date_in_iso8601[17] = { 0 };
* GET_DATE_IN_ISO8601(date_in_iso8601);
* const char amz_date_header[] = "x-amz-date";
* uint32_t amz_date_header_length = strlen(amz_date_header);
* IotHttpsClient_AddHeader(reqHandle, amz_date_header, amz_date_header_length, date_in_iso8601, strlen(date_in_iso8601));
* ...
* IotHttpsClient_SendSync(connHandle, reqHandle, &respHandle, &respInfo, timeout);
* ...
* @endcode
*
* The following header fields are automatically added to the request header buffer and must NOT be added again with
* this routine:
* - Connection: - This header is added to the request when the headers are being sent on the network.
* - User-agent: - This header is added during @ref https_client_function_initializerequest
* - Host: - This header is added during @ref https_client_function_initializerequest
* - Content-Length: - This header is added to the request when the headers are being sent on the network.
*
* The reqHandle is not thread safe. If two threads have the same reqHandle and attempt to add headers at the same
* time, garbage strings may be written to the reqHandle.
*
* @param[in] reqHandle - HTTPS request to write the header line to.
* @param[in] pName - String header field name to write.
* @param[in] nameLen - The length of the header name to write.
* @param[in] pValue - https header value buffer pointer. Do not include token name.
* @param[in] valueLen - length of header value to write.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the header line was successfully added to the header space in #IotHttpsRequestInfo_t.userBuffer.
* - #IOT_HTTPS_INSUFFICIENT_MEMORY if the header line cannot fit into the header buffer.
* - #IOT_HTTPS_INVALID_PARAMETER for NULL parameters or if an attempt to add automatically added headers is made.
*/
/* @[declare_https_client_addheader] */
IotHttpsReturnCode_t IotHttpsClient_AddHeader( IotHttpsRequestHandle_t reqHandle,
char * pName,
uint32_t nameLen,
char * pValue,
uint32_t valueLen );
/* @[declare_https_client_addheader] */
/**
* @brief Writes the request body to the network for the request represented by reqHandle.
*
* This function is intended to be used by an asynchronous request. It must be called within the
* #IotHttpsClientCallbacks_t.writeCallback.
*
* In HTTP/1.1 the headers are sent on the network first before any body can be sent. The auto-generated header
* Content-Length is taken from the len parameter and sent first before the data in parameter pBuf is sent.
* This library does not support Transfer-Encoding: chunked or other requests where the Content-Length is unknown, so
* this function cannot be called more than once in #IotHttpsClientCallbacks_t.writeCallback for an HTTP/1.1 request.
*
* isComplete must always be set to 1 in this current version of the HTTPS client library.
*
* If there are network errors in sending the HTTP headers, then the #IotHttpsClientCallbacks_t.errorCallback will be
* invoked following a return from the #IotHttpsClientCallbacks_t.writeCallback.
*
* <b> Example Asynchronous Code </b>
* @code{c}
* void applicationDefined_writeCallback(void * pPrivData, IotHttpsRequestHandle_t reqHandle)
* {
* ...
* char * writeData[1024];
* IotHttpsClient_WriteRequestBody(reqHandle, writeData, 1024, 1);
* ...
* }
* @endcode
*
* @param[in] reqHandle - identifier of the connection.
* @param[in] pBuf - client write data buffer pointer.
* @param[in] len - length of data to write.
* @param[in] isComplete - This parameter parameter must be set to 1.
*
* @return one of the following:
* - #IOT_HTTPS_OK if write successfully, failure code otherwise.
* - #IOT_HTTPS_MESSAGE_FINISHED if this function is called a second time with the same reqHandle.
* - #IOT_HTTPS_NOT_SUPPORTED if isComplete is set to 0.
* - #IOT_HTTPS_INVALID_PARAMETER if this API is used for a synchronous request.
* - #IOT_HTTPS_NETWORK_ERROR if there was an error sending the headers or body on the network.
* - Please see #IotHttpsReturnCode_t for other failure codes.
*/
/* @[declare_https_client_writerequestbody] */
IotHttpsReturnCode_t IotHttpsClient_WriteRequestBody( IotHttpsRequestHandle_t reqHandle,
uint8_t * pBuf,
uint32_t len,
int isComplete );
/* @[declare_https_client_writerequestbody] */
/**
* @brief Synchronous send of the HTTPS request.
*
* This function blocks waiting for the entirety of sending the request and receiving the response.
*
* If #IotHttpsRequestInfo_t.isNonPersistent is set to true, then the connection will disconnect, close, and clean all
* taken resources automatically after receiving the first response.
*
* See @ref connectionUserBufferMinimumSize for information about the user buffer configured in
* #IotHttpsConnectionInfo_t.userBuffer needed to create a valid connection handle.
*
* To retrieve the response body applications must directly refer #IotHttpsSyncInfo_t.pBody configured in #IotHttpsRequestInfo_t.u.
*
* If the response body does not fit in the configured #IotHttpsSyncInfo_t.pBody, then this function will return with error
* #IOT_HTTPS_MESSAGE_TOO_LARGE. To avoid this issue, the application needs to determine beforehand how large the file
* to download is. This can be done with a HEAD request first, then extracting the "Content-Length" with
* @ref https_client_function_readcontentlength. This could also be done with a GET request with the header
* "Range: bytes=0-0", then extracting the "Content-Range" with @ref https_client_function_readheader. Keep in mind that
* not all HTTP servers support Partial Content responses.
*
* Once a the file size is known, the application can initialize the request with a large
* enough buffer or the application can make a partial content request with the header
* "Range: bytes=N-M", where N is the starting byte requested and M is the ending byte requested.
*
* The response headers as received from the network will be stored in the header buffer space in
* #IotHttpsResponseInfo_t.userBuffer. If the configured #IotHttpsResponseInfo_t.userBuffer is too small
* to fit the headers received, then headers that don't fit will be thrown away. Please see
* #responseUserBufferMinimumSize for information about sizing the #IotHttpsResponseInfo_t.userBuffer.
* To receive feedback on headers discarded, debug logging must be turned on in iot_config.h by setting
* @ref IOT_LOG_LEVEL_HTTPS to @ref IOT_LOG_DEBUG.
*
* Multiple threads must not call this function for the same #IotHttpsRequestHandle_t. Multiple threads can call this
* function for a different #IotHttpsRequestHandle_t, even on the same #IotHttpsConnectionHandle_t. An application must
* wait util a request is fully sent, before scheduling it again. A request is fully sent when this function has returned.
*
* @param[in] connHandle - Handle from an HTTPS connection created with @ref https_client_function_connect.
* @param[in] reqHandle - Handle from a request created with @ref https_client_function_initializerequest.
* @param[out] pRespHandle - HTTPS response handle resulting from a successful send and receive.
* @param[in] pRespInfo - HTTP response configuration information.
* @param[in] timeoutMs - Timeout waiting for the sync request to finish. Set this to 0 to wait forever.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the request was sent and the response was received successfully.
* - #IOT_HTTPS_MESSAGE_TOO_LARGE if the response cannot fit in the configured struct IotHttpsRequestHandle.userBuffer.pBuffer and struct IotHttpsRequestHandle.u.pSyncInfo.pRespData.
* - #IOT_HTTPS_CONNECTION_ERROR if the connection failed.
* - #IOT_HTTPS_INVALID_PARAMETER if there are NULL parameters or the request is asynchronous.
* - #IOT_HTTPS_NETWORK_ERROR if there was an error sending the data on the network.
* - #IOT_HTTPS_PARSING_ERROR if there was an error parsing the HTTP response.
* - #IOT_HTTPS_TIMEOUT_ERROR if the timeoutMs is reached when waiting for a response to the request.
*/
/* @[declare_https_client_sendsync] */
IotHttpsReturnCode_t IotHttpsClient_SendSync( IotHttpsConnectionHandle_t connHandle,
IotHttpsRequestHandle_t reqHandle,
IotHttpsResponseHandle_t * pRespHandle,
IotHttpsResponseInfo_t * pRespInfo,
uint32_t timeoutMs );
/* @[declare_https_client_sendsync] */
/**
* @brief Asynchronous send of the the HTTPS request.
*
* This function will invoke, as needed, each of the non-NULL callbacks configured in #IotHttpsAsyncInfo_t.callbacks
* when the scheduled asynchronous request is progress. Please see #IotHttpsClientCallbacks_t for information on each of
* the callbacks.
*
* After this API is executed, the scheduled async response will store the response headers as received from
* the network, in the header buffer space configured in #IotHttpsResponseInfo_t.userBuffer. If the
* configured #IotHttpsResponseInfo_t.userBuffer is too small to fit the headers received, then headers that don't
* fit will be thrown away. Please see #responseUserBufferMinimumSize for information about sizing the
* #IotHttpsResponseInfo_t.userBuffer.
*
* If #IotHttpsRequestInfo_t.isNonPersistent is set to true, then the connection will disconnect, close, and clean all
* taken resources automatically after receiving the first response.
*
* See @ref connectionUserBufferMinimumSize for information about the user buffer configured in
* #IotHttpsConnectionInfo_t.userBuffer needed to create a valid connection handle.
*
* A #IotHttpsRequestHandle_t cannot be schedule again or reused until the request has finished sending. The request
* has safely finished sending once #IotHttpsClientCallbacks_t.readReadyCallback is invoked. After the
* #IotHttpsClientCallbacks_t.readReadyCallback is invoked the #IotHttpsRequestInfo_t.userBuffer can freed,
* modified, or reused.
*
* @param[in] connHandle - Handle from an HTTPS connection.
* @param[in] reqHandle - Handle from a request created with IotHttpsClient_initialize_request.
* @param[out] pRespHandle - HTTPS response handle.
* @param[in] pRespInfo - HTTP response configuration information.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the request was sent and the response was received successfully.
* - #IOT_HTTPS_MESSAGE_TOO_LARGE if the response cannot fit in the configured
* IotHttpsRequestHandle_t.response_message.headers and IotHttpsRequestHandle_t.response_message.body.
* - #IOT_HTTPS_CONNECTION_ERROR if the connection failed.
* - #IOT_HTTPS_FATAL if there was a grave error with the last async job finishing.
* - #IOT_HTTPS_ASYNC_SCHEDULING_ERROR if there was an error scheduling the asynchronous request.
* - #IOT_HTTPS_INTERNAL_ERROR if there was an internal error with starting an asynchronous request servicing task.
* - #IOT_HTTPS_INVALID_PARAMETER if there were NULL parameters or the request passed in was a synchronous type.
*
*/
/* @[declare_https_client_sendasync] */
IotHttpsReturnCode_t IotHttpsClient_SendAsync( IotHttpsConnectionHandle_t connHandle,
IotHttpsRequestHandle_t reqHandle,
IotHttpsResponseHandle_t * pRespHandle,
IotHttpsResponseInfo_t * pRespInfo );
/* @[declare_https_client_sendasync] */
/**
* @brief Cancel an Asynchronous request.
*
* This will stop an asynchronous request. When an asynchronous request is stopped it will not proceed to do any of
* the following: send headers, send body, receive headers, or receive body. This depends on where in the process
* the request is. For example, if the request is cancelled after sending the headers, then it will not attempt tp
* send the body. A cancelled return code will be returned to the application.
*
* If this is called before the scheduled asynchronous request actually runs, then request will not be sent.
* If this is called during any of the asynchronous callbacks, then the library will stop processing the request when
* the callback returns. This is useful for any error conditions, found during the asynchronous callbacks, where the
* application wants to stop the rest of the request processing.
*
* If the asynchronous request stops processing, the buffers in #IotHttpsRequestInfo_t.userBuffer can be safely freed,
* modified, or reused, only once #IotHttpsClientCallbacks_t.readReadyCallback is invoked.
*
* <b> Example Asynchronous Code </b>
* @code{c}
* void _applicationDefined_appendHeaderCallback(void * pPrivData, IotHttpsRequestHandle_t reqHandle)
* {
* char token[MAX_TOKEN_LENGTH] = { 0 }
* int len = MAX_TOKEN_LENGTH;
* int status = gen_auth_token(token, &len);
* if( status == GEN_TOKEN_FAIL)
* {
* IotHttpsClient_CancelRequestAsync(reqHandle);
* }
* ...
* }
*
* void _applicationDefined_writeCallback(void * pPrivData, IotHttpsRequestHandle_t reqHandle)
* {
* if( application_data_get(writeBuffer, writeBufferLen) == GEN_TOKEN_FAIL)
* {
* IotHttpsClient_CancelRequestAsync(reqHandle);
* }
* ...
* }
* @endcode
*
* @param[in] reqHandle - Request handle associated with the request.
*
* @return One of the following:
* - IOT_HTTPS_OK if the request was successfully cancelled.
*/
/* @[declare_https_client_cancelrequestasync] */
IotHttpsReturnCode_t IotHttpsClient_CancelRequestAsync( IotHttpsRequestHandle_t reqHandle );
/* @[declare_https_client_cancelrequestasync] */
/**
* @brief Cancel an Asynchronous response.
*
* This will stop an asynchronous response. When an asynchronous response is stopped it will not proceed to do any of
* the following: send headers, send body, receive headers, or receive body. This depends on where in the process
* the response is. For example, if the response is cancelled after receiving the headers, then it will not attempt tp
* receive the body. A cancelled return code will be returned to the application.
*
* If this is called during ANY of the asynchronous callbacks, then the library will stop processing the response when
* the callback returns. This is useful for any error conditions, found during the asynchronous callbacks, where the
* application wants to stop the rest of the response processing.
*
* If the asynchronous response stops processing, the buffers configured in #IotHttpsResponseInfo_t.userBuffer can
* be freed, modified, or reused only after the #IotHttpsClientCallbacks_t.responseCompleteCallback in invoked.
*
* <b> Example Asynchronous Code </b>
* @code{c}
*
* void applicationDefined_readReadyCallback(void * pPrivData, IotHttpsResponseHandle_t respHandle, IotHttpsReturnCode_t rc, uint16_t status)
* {
* ...
* if (status != IOT_HTTPS_STATUS_OK)
* {
* IotHttpsClient_CancelResponseAsync(NULL, respHandle);
* }
* ...
* }
* @endcode
*
* @param[in] respHandle - Response handle associated with the response.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the response was successfully cancelled.
*/
/* @[declare_https_client_cancelresponseasync] */
IotHttpsReturnCode_t IotHttpsClient_CancelResponseAsync( IotHttpsResponseHandle_t respHandle );
/* @[declare_https_client_cancelresponseasync] */
/**
* @brief Retrieve the HTTPS response status.
*
* The HTTP response status code is contained in the Status-Line of the response header buffer configured in
* #IotHttpsResponseInfo_t.userBuffer. It is the first line of a standard HTTP response message. If the response
* Status-Line could not fit into #IotHttpsResponseInfo_t.userBuffer, then this function will return an error code.
* Please see #responseUserBufferMinimumSize for information about sizing the #IotHttpsResponseInfo_t.userBuffer.
*
* This routine can be used for both a synchronous and asynchronous response.
*
* <b> Example Synchronous Code </b>
* @code{c}
* ...
* IotHttpsClient_SendSync(connHandle, reqHandle, &respHandle, &respInfo, timeout);
* uint16_t status = 0;
* IotHttpsClient_ReadResponseStatus(respHandle, &status);
* if (status != IOT_HTTPS_STATUS_OK)
* {
* // Handle server response status.
* }
* ...
* @endcode
*
* For an asynchronous response the response status is the status parameter in
* #IotHttpsClientCallbacks_t.readReadyCallback and #IotHttpsClientCallbacks_t.responseCompleteCallback. The application
* should refer to that instead of using this function.
* <b> Example Asynchronous Code </b>
* @code
* void applicationDefined_readReadyCallback(void * pPrivData, IotHttpsResponseHandle_t respHandle, IotHttpsReturnCode_t rc, uint16_t status)
* {
* ...
* if (status != IOT_HTTPS_STATUS_OK)
* {
* // Handle error server response status.
* }
* ...
* }
* @endcode
*
* @param[in] respHandle - Unique handle representing the HTTPS response.
* @param[out] pStatus - Integer status returned by the server.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the response status was successfully read into *status.
* - #IOT_HTTPS_INVALID_PARAMETER for NULL parameters.
* - #IOT_HTTPS_NOT_FOUND if the HTTP response status was not found in the header buffer.
*/
/* @[declare_https_client_readresponsestatus] */
IotHttpsReturnCode_t IotHttpsClient_ReadResponseStatus( IotHttpsResponseHandle_t respHandle,
uint16_t * pStatus );
/* @[declare_https_client_readresponsestatus] */
/**
* @brief Retrieve the HTTPS response content length.
*
* If the "Content-Length" header is available in #IotHttpsResponseInfo_t.userBuffer, this routine extracts that
* value. In some cases the "Content-Length" header is not available. This could be because the server sent a multi-part
* encoded response (For example, "Transfer-Encoding: chunked") or the "Content-Length" header was far down in the list
* of response headers and could not fit into the header buffer configured in #IotHttpsResponseInfo_t.userBuffer.
* Please see #responseUserBufferMinimumSize for information about sizing the #IotHttpsResponseInfo_t.userBuffer.
*
* In the asynchronous request process, the Content-Length is not available until the
* #IotHttpsClientCallbacks_t.readReadyCallback. Before the #IotHttpsClientCallbacks_t.readReadyCallback is invoked, the
* headers are read into as much as can fit in in the header buffer space of #IotHttpsResponseInfo_t.userBuffer.
* <b> Example Asynchronous Code </b>
* @code{c}
* void applicationDefined_readReadyCallback(void * pPrivData, IotHttpsResponseHandle_t respHandle, IotHttpsReturnCode_t rc, uint16_t status)
* {
* uint8_t * readBuffer = NULL;
* uint32_t contentLength = 0;
* IotHttpsClient_ReadContentLength(respHandle, &contentLength);
* readBuffer = (uint8_t*)malloc(contentLength);
* ...
* }
* @endcode
*
* In a synchronous request process, the Content-Length is available after @ref https_client_function_sendsync has
* returned successfully.
* <b> Example Synchronous Code </b>
* @code{c}
* ...
* IotHttpsClient_SendSync(connHandle, reqHandle, &respHandle, &respInfo, timeout);
* uint32_t contentLength = 0;
* IotHttpsClient_ReadContentLength(respHandle, &contentLength);
* printf("Content-Length: %u", (unsigned int)contentLength);
* ...
* @endcode
*
* @param[in] respHandle - Unique handle representing the HTTPS response.
* @param[out] pContentLength - Integer content length from the Content-Length header from the server. If the content
* length is not found this will be 0.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the response body Content-Length was successfully read into contentLength.
* - #IOT_HTTPS_NOT_FOUND if the Content-Length header was not found in the header buffer.
* - #IOT_HTTPS_INVALID_PARAMETER if NULL parameters are passed in.
*/
/* @[declare_https_client_readcontentlength] */
IotHttpsReturnCode_t IotHttpsClient_ReadContentLength( IotHttpsResponseHandle_t respHandle,
uint32_t * pContentLength );
/* @[declare_https_client_readcontentlength] */
/**
* @brief Retrieve the header of interest from the response represented by respHandle.
*
* The response headers as received from the network will be stored in the header buffer space in
* #IotHttpsResponseInfo_t.userBuffer. If the configured #IotHttpsResponseInfo_t.userBuffer is too small to fit
* the headers received, then headers that don't fit will be thrown away. Please see #responseUserBufferMinimumSize for
* information about sizing the #IotHttpsResponseInfo_t.userBuffer.
*
* This routine parses the formatted HTTPS header lines in the header buffer for the header field name specified. If the
* header is not available, then #IOT_HTTPS_NOT_FOUND is returned.
*
* For an asynchronous response, this routine is to be called during the #IotHttpsClientCallbacks_t.readReadyCallback.
* Before the #IotHttpsClientCallbacks_t.readReadyCallback is invoked, the
* headers are read into as much as can fit in in the header buffer space of #IotHttpsResponseInfo_t.userBuffer.
* <b> Example Asynchronous Code </b>
* @code{c}
* void applicationDefined_readReadyCallback(void * pPrivData, IotHttpsResponseHandle_t respHandle, IotHttpsReturnCode_t rc, uint16_t status)
* {
* ...
* char valueBuf[64];
* const char contentTypeName[] = "Content-Type";
* uint32_t contentTypeNmeLength = strlen(contentTypeName);
* IotHttpsClient_ReadHeader(respHandle, contentTypeName, contentTypeNameLength, valueBuf, sizeof(valueBuf));
* ...
* }
* @endcode
*
* For a syncrhonous response, this routine is to be called after @ref https_client_function_sendsync has
* returned successfully.
* <b> Example Synchronous Code </b>
* @code{c}
* ...
* IotHttpsClient_SendSync(&connHandle, reqHandle, &respHandle, &respInfo, timeout);
* char valueBuf[10];
* const char contentTypeName[] = "Content-Type";
* uint32_t contentTypeNmeLength = strlen(contentTypeName);
* IotHttpsClient_ReadHeader(respHandle, contentTypeName, contentTypeNmeLength, valueBuf, sizeof(valueBuf));
* uint32_t length = strtoul(valueBuf, NULL, 10);
* ...
* @endcode
*
* @param[in] respHandle - Unique handle representing the HTTPS response.
* @param[in] pName - HTTPS Header field name we want the value of. This must be NULL terminated.
* @param[in] nameLen - The length of the name string.
* @param[out] pValue - Buffer to hold the HTTPS field's value. The returned value will be NULL terminated
* and therfore the buffer must be large enough to hold the terminating NULL character.
* @param[in] valueLen - The length of the value buffer.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the header's corresponding value was read into *pValue.
* - #IOT_HTTPS_NOT_FOUND if the header value was not found.
* - #IOT_HTTPS_INVALID_PARAMETER if the respHandle is not valid, there is no response saved or the handle does not exist.
* - #IOT_HTTPS_INSUFFICIENT_MEMORY if the value is too large to fit into *pValue.
*/
/* @[declare_https_client_readheader] */
IotHttpsReturnCode_t IotHttpsClient_ReadHeader( IotHttpsResponseHandle_t respHandle,
char * pName,
uint32_t nameLen,
char * pValue,
uint32_t valueLen );
/* @[declare_https_client_readheader] */
/**
* @brief Read the HTTPS response body from the network.
*
* This is intended to be used with an asynchronous response, this is to be invoked during the
* #IotHttpsClientCallbacks_t.readReadyCallback to read data directly from the network into pBuf.
* <b> Example Asynchronous Code </b>
* @code{c}
* void applicationDefined_readReadyCallback(void * pPrivData, IotHttpsRequestHandle_t handle, IotHttpsReturnCode_t rc, uint16_t status)
* {
* ...
* char * myBuf = STORE_ADDRESS;
* uint32_t len = STORE_READ_SIZE;
* IotHttpsClient_ReadResponseBody(handle, myBuf, &len);
* ...
* }
* @endcode
*
* For a syncrhonous response, to retrieve the response body applications must directly refer to the memory configured
* to receive the response body: #IotHttpsSyncInfo_t.pBody in #IotHttpsResponseInfo_t.pSyncInfo. Otherwise this function
* will return an #IOT_HTTPS_INVALID_PARAMETER error code. This function is intended to read the response entity body
* from the network and the synchronous response process handles all of that in @ref https_client_function_sendsync.
*
* @param[in] respHandle - Unique handle representing the HTTPS response.
* @param[out] pBuf - Pointer to the response body memory location. This is not a char* because the body may have binary data.
* @param[in,out] pLen - The length of the response to read. This should not exceed the size of the buffer that we are reading into. This will be replace with the amount of data read upon return.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the response body was successfully retrieved.
* - #IOT_HTTPS_INVALID_PARAMETER if there are NULL parameters or if the response is a synchronous type.
* - #IOT_HTTPS_NETWORK_ERROR if there was an error sending the data on the network.
* - #IOT_HTTPS_PARSING_ERROR if there was an error parsing the HTTP response.
*/
/* @[declare_https_client_readresponsebody] */
IotHttpsReturnCode_t IotHttpsClient_ReadResponseBody( IotHttpsResponseHandle_t respHandle,
uint8_t * pBuf,
uint32_t * pLen );
/* @[declare_https_client_readresponsebody] */
#endif /* IOT_HTTPS_CLIENT_ */

View file

@ -0,0 +1,95 @@
/*
* Amazon FreeRTOS HTTPS Client V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_https_utils.h
* @brief User facing HTTPS Client library utilities.
*/
#ifndef IOT_HTTPS_UTILS_H_
#define IOT_HTTPS_UTILS_H_
#include "types/iot_https_types.h"
/*-----------------------------------------------------------*/
/**
* @brief Retrieve the path from the input URL.
*
* This function retrieves the location and length of the path from within the
* input the URL. The query is not included in the length returned.
*
* The URL MUST start with "http://" or "https://" to find the path.
*
* For example, if the URL is:
* pUrl = "https://www.somewebsite.com/path/to/item.txt?optionalquery=stuff"
*
* *pPath = "/path/to/item.txt?optionalquery=stuff"
* *pPathLen = 17
*
* @param[in] pUrl - URL string to parse.
* @param[in] urlLen - The length of the URL string input.
* @param[out] pPath - pointer within input url that the path starts at.
* @param[out] pPathLen - Length of the path.
*
* - #IOT_HTTPS_OK if the path was successfully parsed.
* - #IOT_HTTPS_PARSING_ERROR if there was an error parsing the URL.
* - #IOT_HTTPS_NOT_FOUND if the path was not found.
*/
IotHttpsReturnCode_t IotHttpsClient_GetUrlPath( const char * pUrl,
size_t urlLen,
const char ** pPath,
size_t * pPathLen );
/**
* @brief Retrieve the Address from the input URL.
*
* This function retrieves the location and length of the address from within
* the input URL. The path and query are not included in the length returned.
*
* The URL MUST start with "http://" or "https://" to find the address.
*
* For example, if the URL is:
* pUrl = "https://www.somewebsite.com/path/to/item.txt?optionalquery=stuff"
*
* *pAddress = "www.somewebsite.com/path/to/item.txt?optionalquery=stuff"
* *pAddressLen = 19
*
* @param[in] pUrl - URL string to parse.
* @param[in] urlLen - The length of the URL string input.
* @param[out] pAddress - pointer within input url that the address starts at.
* @param[out] pAddressLen - Length of the address.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the path was successfully parsed.
* - #IOT_HTTPS_PARSING_ERROR if there was an error parsing the URL.
* - #IOT_HTTPS_NOT_FOUND if the address was not found.
*/
IotHttpsReturnCode_t IotHttpsClient_GetUrlAddress( const char * pUrl,
size_t urlLen,
const char ** pAddress,
size_t * pAddressLen );
#endif /* IOT_HTTPS_UTILS_H_ */

View file

@ -0,0 +1,907 @@
/*
* Amazon FreeRTOS HTTPS Client V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_https_types.h
* @brief Types of the HTTPS Client library.
*/
#ifndef IOT_HTTPS_TYPES_H_
#define IOT_HTTPS_TYPES_H_
/* The config header is always included first. */
#include "iot_config.h"
/* C standard includes. */
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/* Type includes. */
#include "types/iot_platform_types.h"
/* Platform network include. */
#include "platform/iot_network.h"
/*------------------------- HTTPS defined constants --------------------------*/
/**
* @constantspage{https_client,HTTPS Client library}
*
* @section https_minimum_user_buffer_sizes HTTPS Client Minimum User Buffer Sizes
* @brief Variables calculating the size of #IotHttpsUserBuffer_t.bufferLen needed for the request, response, and
* connection.
*
* @note These user buffer minimum values may change at any time in future versions, but their names will remain the
* same.
* - @ref requestUserBufferMinimumSize <br>
* @copybrief requestUserBufferMinimumSize
* - @ref responseUserBufferMinimumSize <br>
* @copybrief responseUserBufferMinimumSize
* - @ref connectionUserBufferMinimumSize <br>
* @copybrief connectionUserBufferMinimumSize
*
* @section https_connection_flags HTTPS Client Connection Flags
* @brief Flags that modify the behavior of the HTTPS Connection.
*
* Flags should be bitwise-ORed with each other to change the behavior of @ref https_client_function_sendasync and
* @ref https_client_function_sendsync. These flags are set in #IotHttpsConnectionInfo_t.flags.
*
* @note The values of flags may change at any time in future versions, but their names will remain the same.
*
* @section https_initializers HTTP Initializers
* @brief Provide default values for the data types of the HTTP Client Library.
*
* @snippet this define_https_initializers
*
* All user-facing data types of the HTTPS Client library should be initialized using one of the following.
*
* @warning Failing to initialize an HTTPS Client data type with the appropriate initializer may result in undefined
* behavior.
* @note The initializers may change at any time in future versions, but their names will remain the same.
*
* <b>Example</b>
* @code{c}
* IotHttpsConnectionHandle_t connHandle = IOT_HTTPS_CONNECTION_HANDLE_INITIALIZER;
* IotHttpsRequestHandle_t reqHandle = IOT_HTTPS_REQUEST_HANDLE_INITIALIZER;
* IotHttpsResponseHandle_t respHandle = IOT_HTTPS_RESPONSE_HANDLE_INITIALIZER;
* IotHttpsUserBuffer_t userBuffer = IOT_HTTPS_USER_BUFFER_INITIALIZER;
* IotHttpsSyncInfo_t syncInfoReq = IOT_HTTPS_SYNC_INFO_INITIALIZER;
* IotHttpsSyncInfo_t syncInfoResp = IOT_HTTPS_SYNC_INFO_INITIALIZER;
* IotHttpsConnectionInfo_t connInfo = IOT_HTTPS_CONNECTION_INFO_INITIALIZER;
* IotHttpsRequestInfo_t reqInfo = IOT_HTTPS_REQUEST_INFO_INITIALIZER
* IotHttpsResponseInfo_t respInfo = IOT_HTTPS_RESPONSE_INFO_INITIALIZER
* @endcode
*
* @section http_constants_connection_flags HTTPS Client Connection Flags
* @brief Flags that modify the behavior the HTTPS connection.
* - #IOT_HTTPS_IS_NON_TLS_FLAG <br>
* @copybrief IOT_HTTPS_IS_NON_TLS_FLAG
* - #IOT_HTTPS_DISABLE_SNI <br>
* @copybrief IOT_HTTPS_DISABLE_SNI
*/
/**
* @brief The minimum user buffer size for the HTTP request context and headers.
*
* This helps to calculate the size of the buffer needed for #IotHttpsRequestInfo_t.userBuffer.
*
* This buffer size is calculated to fit the HTTP Request-Line and the default headers. It does not account for the
* length of the path in the Request-Line nor does it account for the length of the host name. It also does not account
* for extra headers that the application may add. These sizes need to be accounted for by the application when
* assigning a buffer.
*
* A typical value for sizing the request user buffer for the request context is 512 bytes. See the example below.
* @code{c}
* uint8_t requestUserBuffer[512] = { 0 };
* IotHttpsRequestInfo_t requestInfo = IOT_HTTPS_REQUEST_INFO_INITIALIZER;
* requestInfo.userBuffer.pBuffer = requestUserBuffer;
* @endcode
*
* By the application providing the memory for the internal context, no memory is needed to be allocated internally to
* the library for the internal context. The application has control over the memory allocation related to the request,
* response, and connection.
*/
extern const uint32_t requestUserBufferMinimumSize;
/**
* @brief The minimum user buffer size for the HTTP response context and headers.
*
* This helps to calculate the size of the buffer needed for #IotHttpsResponseInfo_t.userBuffer.
*
* The buffer size is calculated to fit the HTTP response context only. It does not account for the HTTP response status
* line. It does not account for any HTTP response headers. If the buffer assigned to
* #IotHttpsResponseInfo_t.userBuffer is of this minimum size, then the response Status-Line and the response headers
* will not be stored. These sizes need to be accounted for by the application when assigning a buffer.
*
* If the response Status-Line and response headers cannot fit into #IotHttpsResponseInfo_t.userBuffer, then after a
* call to @ref https_client_function_sendsync, calls to @ref https_client_function_readresponsestatus,
* @ref https_client_function_readcontentlength, and @ref https_client_function_readheader will return a failure code.
*
* A typical value for sizing the response user buffer for the response context is 512 bytes. See the example below.
* @code{c}
* uint8_t responseUserBuffer[512] = { 0 };
* IotHttpsResponseInfo_t responseInfo = IOT_HTTPS_RESPONSE_INFO_INITIALIZER;
* responseInfo.userBuffer.pBuffer = responseUserBuffer;
* @endcode
*
* By the application providing the memory for the internal context, no memory is needed to be allocated internally to
* the library for the internal context. The application has control over the memory allocation related to the request,
* response, and connection.
*/
extern const uint32_t responseUserBufferMinimumSize;
/**
* @brief The minimum user buffer size for the HTTP connection context and headers.
*
* This helps to calculate the size of the buffer needed for #IotHttpsConnectionInfo_t.userBuffer.
*
* The buffer size is calculated to fit the HTTP connection context only. The buffer assigned by the application must be
* at least this size.
*
* A typical value for sizing the request user buffer for the connection context is 512 bytes. See the example below.
* @code{c}
* uint8_t connectionUserBuffer[512] = { 0 };
* IotHttpsConnectionInfo_t connectionInfo = IOT_HTTPS_CONNECTION_INFO_INITIALIZER;
* connectionInfo.userBuffer.pBuffer = connectionUserBuffer;
* @endcode
*
* By the application providing the memory for the internal context, no memory is needed to be allocated internally to
* the library for the internal context. The application has control over the memory allocation related to the request,
* response, and connection.
*/
extern const uint32_t connectionUserBufferMinimumSize;
/**
* @brief Flag for #IotHttpsConnectionInfo_t that disables TLS.
*
* Set this bit in #IotHttpsConnectionInfo_t.flags to disable use of TLS when the connection is created. This library
* creates secure connections by default.
*/
#define IOT_HTTPS_IS_NON_TLS_FLAG ( 0x00000001 )
/**
* @brief Flag for #IotHttpsConnectionInfo_t that disables Server Name Indication (SNI).
*
* Set this bit #IotHttpsConnectionInfo_t.flags to disable SNI. SNI is enabled by default in this library. When SNI is
* enabled #IotHttpsConnectionInfo_t.pAddress will be used for the server name verification.
*/
#define IOT_HTTPS_DISABLE_SNI ( 0x00000008 )
/* @[define_https_initializers] */
/** @brief Initializer for #IotHttpsConnectionHandle_t. */
#define IOT_HTTPS_CONNECTION_HANDLE_INITIALIZER NULL
/** @brief Initializer for #IotHttpsRequestHandle_t. */
#define IOT_HTTPS_REQUEST_HANDLE_INITIALIZER NULL
/** @brief Initializer for #IotHttpsResponseHandle_t. */
#define IOT_HTTPS_RESPONSE_HANDLE_INITIALIZER NULL
/** @brief Initializer for #IotHttpsUserBuffer_t. */
#define IOT_HTTPS_USER_BUFFER_INITIALIZER { 0 }
/** @brief Initializer for #IotHttpsSyncInfo_t. */
#define IOT_HTTPS_SYNC_INFO_INITIALIZER { 0 }
/** @brief Initializer for #IotHttpsAsyncInfo_t. */
#define IOT_HTTPS_ASYNC_INFO_INITIALIZER { 0 }
/** @brief Initializer for #IotHttpsConnectionInfo_t. */
#define IOT_HTTPS_CONNECTION_INFO_INITIALIZER { 0 }
/** @brief Initializer for #IotHttpsRequestInfo_t. */
#define IOT_HTTPS_REQUEST_INFO_INITIALIZER { 0 }
/** @brief Initializer for #IotHttpsResponseInfo_t. */
#define IOT_HTTPS_RESPONSE_INFO_INITIALIZER { 0 }
/* @[define_https_initializers] */
/* Network include for the network types below. */
#include "platform/iot_network.h"
/**
* @brief Type for the network interface containing the operations to send, receive, connect, and disconnect from
* the network.
*/
#define IOT_HTTPS_NETWORK_INTERFACE_TYPE const IotNetworkInterface_t *
/*---------------------------- HTTPS handle types ----------------------------*/
/**
* @handles{https_client,HTTPS Client library}
*/
/**
* @ingroup https_client_datatypes_handles
* @brief Opaque handle of an HTTP connection.
*
* A connection handle is needed to send many requests over a single persistent connection. This handle is valid after
* a successful call to @ref https_client_function_connect or @ref https_client_function_sendsync or
* @ref https_client_function_sendasync. A variable of this type is passed to @ref https_client_function_sendsync,
* @ref https_client_function_sendasync, and @ref https_client_function_disconnect to identify which connection that
* function acts on.
*
* A call to @ref https_client_function_disconnect makes a connection handle invalid. Once @ref https_client_function_disconnect
* returns, the connection handle should no longer be used. The application must call @ref https_client_function_connect
* again to retrieve a new handle and a new connection.
*
* Typical web servers disconnect the client in around 30-60 seconds. The application needs to be aware of this, when
* taking time between requests in a persistent connection.
*
* A connection handle is not thread safe. Multiple threads cannot connect and disconnect with the same handle at the
* same time.
*
* Multiple threads can call @ref https_client_function_sendasync or @ref https_client_function_sendsync with the same
* connection handle.
*/
typedef struct _httpsConnection * IotHttpsConnectionHandle_t;
/**
* @ingroup https_client_datatypes_handles
* @brief Opaque handle of an HTTP request.
*
* Having a separate handle for the HTTP request allows the application to re-use a request.
*
* This handle is valid after a successful call to @ref https_client_function_initializerequest. A variable of this type
* is passed to @ref https_client_function_sendasync or @ref https_client_function_sendsync.
*
* A request handle cannot be sent on multiple connections at the same time.
*
* A request handle is not thread safe. Multiple threads cannot write headers to the same request handle.
*/
typedef struct _httpsRequest * IotHttpsRequestHandle_t;
/**
* @ingroup https_client_datatypes_handles
* @brief Opaque handle of an HTTP response.
*
* This handle is valid after a successful call to @ref https_client_function_sendsync or
* @ref https_client_function_sendasync. A variable of this type is passed to
* @ref https_client_function_readresponsestatus, @ref https_client_function_readcontentlength,
* @ref https_client_function_readheader, and @ref https_client_function_readresponsebody.
*
* When returned from @ref https_client_function_sendsync or @ref https_client_function_sendasync, there is an
* associated #IotHttpsRequestHandle_t. If the #IotHttpsRequestHandle_t associated with this response is re-initialized
* with @ref https_client_function_initializerequest, then this response handle is no longer valid.
*
* A response handle is not thread safe. Multiple threads cannot read the headers in a response at the same time.
*/
typedef struct _httpsResponse * IotHttpsResponseHandle_t;
/*-------------------------- HTTPS enumerated types --------------------------*/
/**
* @enums{https_client,HTTPS Client library}
*/
/**
* @ingroup https_client_datatypes_enums
* @brief Return codes of [HTTPS Client functions](@ref https_client_functions).
*/
typedef enum IotHttpsReturnCode
{
/**
* @brief Returned for a successful operation.
*/
IOT_HTTPS_OK = 0,
/**
* @brief An invalid parameter was passed into an API function.
*/
IOT_HTTPS_INVALID_PARAMETER = 101,
/**
* @brief Invalid payload.
*/
IOT_HTTPS_INVALID_PAYLOAD = 102,
/**
* @brief HTTPS message was too large to fit into a configured synchronous body buffer.
*/
IOT_HTTPS_MESSAGE_TOO_LARGE = 103,
/**
* @brief Overflow occurred somewhere.
*/
IOT_HTTPS_OVERFLOW = 104,
/**
* @brief A buffer provided could not hold data required by the library.
*/
IOT_HTTPS_INSUFFICIENT_MEMORY = 105,
/**
* @brief Queue full.
*/
IOT_HTTPS_QUEUE_FULL = 106,
/**
* @brief Operation retry.
*/
IOT_HTTPS_RETRY = 107,
/**
* @brief Could not find an item specified by an API.
*
* Returned for not being able to find the address in a URL, the path in a URL, or a header field from the response
* headers.
*/
IOT_HTTPS_NOT_FOUND = 108,
/**
* @brief The HTTP request message was finished being written and we cannot write more with @ref https_client_function_writerequestbody.
*/
IOT_HTTPS_MESSAGE_FINISHED = 109,
/**
* @brief An error occurred internally to the library.
*/
IOT_HTTPS_INTERNAL_ERROR = 201,
/**
* @brief A network error occurred.
*/
IOT_HTTPS_NETWORK_ERROR = 202,
/**
* @brief A network connection error occurred.
*/
IOT_HTTPS_CONNECTION_ERROR = 203,
/**
* @brief A stream error occurred.
*/
IOT_HTTPS_STREAM_ERROR = 204,
/**
* @brief An authentication error occurred.
*/
IOT_HTTPS_AUTHENTICATION_ERROR = 205,
/**
* @brief A TLS error occurred.
*/
IOT_HTTPS_TLS_ERROR = 206,
/**
* @brief An error occurred during the user callback.
*/
IOT_HTTPS_USER_CALLBACK_ERROR = 207,
/**
* @brief The synchronous response could not be received in the specified timeout in @ref https_client_function_sendsync.
*/
IOT_HTTPS_TIMEOUT_ERROR = 208,
/**
* @brief An error in the HTTP protocol.
*/
IOT_HTTPS_PROTOCOL_ERROR = 209,
/**
* @brief The HTTPS request send was cancelled.
*/
IOT_HTTPS_SEND_ABORT = 210,
/**
* @brief The HTTPS response receiving was cancelled.
*/
IOT_HTTPS_RECEIVE_ABORT = 211,
/**
* @brief The asynchronous request had an error being scheduled.
*/
IOT_HTTPS_ASYNC_SCHEDULING_ERROR = 212,
/**
* @brief There was an error parsing the HTTP response.
*/
IOT_HTTPS_PARSING_ERROR = 213,
/**
* @brief Fatal HTTP library error.
*/
IOT_HTTPS_FATAL = 901,
/**
* @brief The connection is busy and cannot be cleaned up.
*
* The connection was closed, but @ref https_client_function_disconnect must be called again to cleanup connection
* resources.
*/
IOT_HTTPS_BUSY = 902,
/**
* @brief Try again.
*/
IOT_HTTPS_TRY_AGAIN = 903,
/**
* @brief Data exists.
*/
IOT_HTTPS_DATA_EXIST = 904,
/**
* @brief The operation on the public API is not supported.
*/
IOT_HTTPS_NOT_SUPPORTED = 905
} IotHttpsReturnCode_t;
/**
* @ingroup https_client_datatypes_enums
* @brief Types of HTTP methods.
*
* The HTTP method is configured in #IotHttpsRequestInfo_t.method.
*/
typedef enum IotHttpsMethod
{
IOT_HTTPS_METHOD_GET = 0, /* Client-to-server method GET */
IOT_HTTPS_METHOD_HEAD, /* Client-to-server method HEAD */
IOT_HTTPS_METHOD_PUT, /* Client-to-server method PUT */
IOT_HTTPS_METHOD_POST /* Client-to-server method POST. */
} IotHttpsMethod_t;
/**
* @ingroup https_client_datatypes_enums
* @brief Types of standard HTTP Response status codes.
*
* These status codes are taken from RFC 2616. Please see RFC 2616 for a description of each response status.
*/
enum IotHttpsResponseStatus
{
IOT_HTTPS_STATUS_CONTINUE = 100,
IOT_HTTPS_STATUS_SWITCHING_PROTOCOLS,
IOT_HTTPS_STATUS_OK = 200,
IOT_HTTPS_STATUS_CREATED,
IOT_HTTPS_STATUS_ACCEPTED,
IOT_HTTPS_STATUS_NON_AUTHORITIVE_INFORMATION,
IOT_HTTPS_STATUS_NO_CONTENT,
IOT_HTTPS_STATUS_RESET_CONTENT,
IOT_HTTPS_STATUS_PARTIAL_CONTENT,
IOT_HTTPS_STATUS_MULTIPLE_CHOICES = 300,
IOT_HTTPS_STATUS_MOVED_PERMANENTLY,
IOT_HTTPS_STATUS_FOUND,
IOT_HTTPS_STATUS_SEE_OTHER,
IOT_HTTPS_STATUS_NOT_MODIFIED,
IOT_HTTPS_STATUS_USE_PROXY,
IOT_HTTPS_STATUS_UNUSED,
IOT_HTTPS_STATUS_TEMPORARY_REDIRECT,
IOT_HTTPS_STATUS_BAD_REQUEST = 400,
IOT_HTTPS_STATUS_UNAUTHORIZED,
IOT_HTTPS_STATUS_PAYMENT_REQUIRED,
IOT_HTTPS_STATUS_FORBIDDEN,
IOT_HTTPS_STATUS_NOT_FOUND,
IOT_HTTPS_STATUS_METHOD_NOT_ALLOWED,
IOT_HTTPS_STATUS_NOT_ACCEPTABLE,
IOT_HTTPS_STATUS_PROXY_AUTHENTICATION_REQUIRED,
IOT_HTTPS_STATUS_REQUEST_TIMEOUT,
IOT_HTTPS_STATUS_CONFLICT,
IOT_HTTPS_STATUS_GONE,
IOT_HTTPS_STATUS_LENGTH_REQUIRED,
IOT_HTTPS_STATUS_PRECONDITION_FAILED,
IOT_HTTPS_STATUS_REQUEST_ENTITY_TOO_LARGE,
IOT_HTTPS_STATUS_REQUEST_URI_TOO_LONG,
IOT_HTTPS_STATUS_UNSUPPORTED_MEDIA_TYPE,
IOT_HTTPS_STATUS_REQUEST_RANGE_NOT_SATISFIABLE,
IOT_HTTPS_STATUS_EXPECTATION_FAILED,
IOT_HTTPS_STATUS_INTERNAL_SERVER_ERROR = 500,
IOT_HTTPS_STATUS_NOT_IMPLEMENTED,
IOT_HTTPS_STATUS_BAD_GATEWAY,
IOT_HTTPS_STATUS_SERVICE_UNAVAILABLE,
IOT_HTTPS_STATUS_GATEWAY_TIMEOUT,
IOT_HTTPS_STATUS_HTTP_VERSION_NOT_SUPPORTED
};
/*------------------------- HTTPS parameter structs --------------------------*/
/**
* @paramstructs{https_client,HTTPS Client library}
*/
/**
* @ingroup https_client_datatypes_paramstructs
*
* @brief HTTPS Client library callbacks for asynchronous requests.
*
* @paramfor @ref https_client_function_initializerequest
*
* This type is a parameter in #IotHttpsResponseInfo_t.
*
* If any of the members in this type are set to NULL, then they will not be invoked during the asynchronous
* request/response process.
*
* See @ref Asynchronous_Callback_Order for the order of the order of the callbacks and when they will be invoked.
*/
typedef struct IotHttpsClientCallbacks
{
/**
* @brief User-provided callback function signature for appending a header to current asynchronous request.
*
* If this is set to NULL, then it will not be invoked.
* See @ref https_client_function_addheader for more information on adding a header in this callback.
*
* Appending the header when request is in progress is good for things like time limited authentication tokens.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] reqHandle - The handle for the current HTTP request in progress.
*/
void ( * appendHeaderCallback )( void * pPrivData,
IotHttpsRequestHandle_t reqHandle );
/**
* @brief User-provided callback function signature for writing data to the network for a current asynchronous
* request.
*
* If this is set to NULL, then it will not be invoked.
* See @ref https_client_function_writerequestbody for more information on writing request body.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] reqHandle - The handle for the current HTTP request in progress.
*/
void ( * writeCallback )( void * pPrivData,
IotHttpsRequestHandle_t reqHandle );
/**
* @brief User-provided callback function signature for reading data from the network for a current asynchronous
* response.
*
* The network indicated that after sending the associated request, the response is available for reading.
* If this is set to NULL, then it will not be invoked and any response body received will be ignored.
* See @ref https_client_function_readresponsebody for more information about reading the response body in this
* callback.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] respHandle - The handle for the current HTTP response in progress.
* @param[in] rc - A return code indicating any errors before this callback was invoked.
* @param[in] status - The HTTP response status code of the current response in progress.
*/
void ( * readReadyCallback )( void * pPrivData,
IotHttpsResponseHandle_t respHandle,
IotHttpsReturnCode_t rc,
uint16_t status );
/**
* @brief User-provided callback function signature to indicate that the asynchronous response is completed.
*
* If this is set to NULL, then it will not be invoked.
*
* This callback is invoked when the response is fully received from the network and the request/response pair is
* complete.
* If there was an error in sending the request or an error in receiving the associated response, this callback will
* be invoked, if the error caused the request or associated response to finish.
* #IotHttpsClientCallbacks_t.errorCallback will be invoked first before this callback.
* This callback is invoked to let the application know that memory used by #IotHttpsRequestInfo_t.userBuffer and
* #IotHttpsResponseInfo_t.userBuffer can be freed, modified, or reused.
*
* For a non-persistent connection, the connection will be closed first before invoking this callback.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] respHandle - The handle for the current HTTP response in progress.
* @param[in] rc - A return code indicating any errors before this callback was invoked.
* @param[in] status - The HTTP response status code of the current response in progress.
*/
void ( * responseCompleteCallback )( void * pPrivData,
IotHttpsResponseHandle_t respHandle,
IotHttpsReturnCode_t rc,
uint16_t status );
/**
* @brief User-provided callback function signature to indicate that the connection has been close in an asynchronous
* request process.
*
* If this is set to NULL, then it will not be invoked.
* If there are errors during sending/receiving in the asynchronous process, the connection is not automatically
* closed. If the server closes the connection during the asynchronous process, this callback is not invoked.
* This callback is invoked only if the connection was flagged as non-persistent in
* #IotHttpsConnectionInfo_t.flags.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] connHandle - The handle for the current HTTP connection.
* @param[in] rc - A return code indicating any errors before this callback was invoked.
*/
void ( * connectionClosedCallback )( void * pPrivData,
IotHttpsConnectionHandle_t connHandle,
IotHttpsReturnCode_t rc );
/**
* @brief User-provided callback function signature to indicate that an error occurred during the asynchronous
* request process.
*
* If respHandle is NULL, then reqHandle will not be NULL and vise-versa. This signals which handle the error
* occurred and if it is during the sending or receiving.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] respHandle - The handle for the current HTTP response.
* @param[in] reqHandle - The handle for the current HTTP request.
* @param[in] rc - A return code indicating any errors before this callback was invoked.
*/
void ( * errorCallback )( void * pPrivData,
IotHttpsRequestHandle_t reqHandle,
IotHttpsResponseHandle_t respHandle,
IotHttpsReturnCode_t rc );
} IotHttpsClientCallbacks_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief User-provided buffer for storing the HTTPS headers and library internal context.
*
* @paramfor @ref https_client_function_initializerequest.
*
* The user buffer is configured in #IotHttpsConnectionInfo_t.userBuffer, #IotHttpsRequestInfo_t.userBuffer and
* #IotHttpsResponseInfo_t.userBuffer.
*
* The minimum size that the buffer must be configured to is indicated by requestUserBufferMinimumSize,
* responseUserBufferMinimumSize, connectionUserBufferMinimumSize.
*/
typedef struct IotHttpsUserBuffer
{
uint8_t * pBuffer; /**< @brief Application provided buffer pointer. */
uint32_t bufferLen; /**< @brief The length of the application provided buffer. */
} IotHttpsUserBuffer_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief HTTPS Client synchronous request information.
*
* @paramfor @ref https_client_function_initializerequest, @ref https_client_function_sendsync,
* @ref https_client_function_sendasync
*
* This structure is configured in #IotHttpsRequestInfo_t.u and #IotHttpsResponseInfo_t.
*
* A synchronous request will block until the response is fully received from the network.
* This structure defines memory locations to store the response body.
*/
typedef struct IotHttpsSyncRequestInfo
{
/**
* Pointer to the HTTP message body.
*
* For a request this is the file or data we want to send. The data is separated from the headers for the
* flexibility to point to an already established file elsewhere in memory.
*
* For a response this is where to receive the response entity body.
* If the length of the buffer provided to store the response body is smaller than the amount of body received,
* then @ref https_client_function_sendsync will return a IOT_HTTPS_INSUFFICIENT_MEMORY error code. Although an error
* was returned, the first #IotHttpsSyncInfo_t.bodyLen of the response received on the network will
* still be available in the buffer.
*/
uint8_t * pBody;
uint32_t bodyLen; /**< @brief The length of the HTTP message body. */
} IotHttpsSyncInfo_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief HTTPS Client asynchronous request information.
*
* @paramfor @ref https_client_function_initializerequest.
*
* This is parameter in #IotHttpsRequestInfo_t.u.
*
* An asynchronous request will ask the application for headers and body right before the request is ready
* to be sent onto the network.
* An asynchronous request will have the application read headers and body as soon as the response is received
* on the network.
*/
typedef struct IotHttpsAsyncInfo
{
/**
* @brief Callbacks are used for an asynchronous request.
* See #IotHttpsClientCallbacks_t for more information.
*/
IotHttpsClientCallbacks_t callbacks;
void * pPrivData; /**< @brief User private data to provide context to the asynchronous callbacks. */
} IotHttpsAsyncInfo_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief HTTP connection configuration.
*
* @paramfor @ref https_client_function_connect or @ref https_client_function_sendsync or
* @ref https_client_function_sendasync.
*
* This parameter is used to connection in @ref https_client_function_connect.
*
* @note The lengths of the strings in this struct should not include the NULL
* terminator. Strings in this struct do not need to be NULL-terminated.
*/
typedef struct IotHttpsConnectionInfo
{
/**
* @brief Remote server address that is DNS discoverable.
*
* For example: avs-alexa-na.amazon.com.
*/
const char * pAddress;
uint32_t addressLen; /**< @brief remote address length. */
uint16_t port; /**< @brief Remote port number */
/**
* @brief Flags to configure the HTTPS connection.
*
* See @ref https_connection_flags for the available flags.
*
* Unknown flags are ignored.
*/
uint32_t flags; /**< @brief Flags to configure the HTTPS connection. */
/**
* @brief Timeout waiting for a response from the network in milliseconds.
*
* If this is set to zero, it will default to @ref IOT_HTTPS_RESPONSE_WAIT_MS.
*/
uint32_t timeout;
const char * pCaCert; /**< @brief Server trusted certificate store for this connection. */
uint32_t caCertLen; /**< @brief Server trusted certificate store size. */
const char * pClientCert; /**< @brief Client certificate store for this connection. */
uint32_t clientCertLen; /**< @brief Client certificate store size. */
const char * pPrivateKey; /**< @brief Client private key store for this connection. */
uint32_t privateKeyLen; /**< @brief Client private key store size. */
/**
* @brief String of all the ALPN protocols separated by commas needed for this connection.
*
* For the protocols needed for the AWS Iot Message broker endpoint please see:
* https://docs.aws.amazon.com/iot/latest/developerguide/protocols.html
*/
char * pAlpnProtocols;
uint32_t alpnProtocolsLen; /**< @brief ALPN protocol string length. */
/**
* @brief User buffer to store the internal connection context.
*
* See @ref connectionUserBufferMinimumSize for information about the user buffer configured in
* #IotHttpsConnectionInfo_t.userBuffer needed to create a valid connection handle.
*/
IotHttpsUserBuffer_t userBuffer;
/**
* @brief The IOT network abstraction interface.
*
* This contains the interface to connect, disconnect, send data, and receive data from the network.
*
* In Amazon FreeRTOS this should be of the type IotNetworkInterface_t.
*/
IOT_HTTPS_NETWORK_INTERFACE_TYPE pNetworkInterface;
} IotHttpsConnectionInfo_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief HTTP request configuration.
*
* @paramfor @ref https_client_function_initializerequest.
*
* This parameter is used to configure the request in https_client_function_initializerequest.
*
* @note The lengths of the strings in this struct should not include the NULL
* terminator. Strings in this struct do not need to be NULL-terminated.
*/
typedef struct IotHttpsRequestInfo
{
/**
* @brief The absolute path to the HTTP request object.
*
* The absolute path includes the path to the file AND the optional query.
* An example URI path: "/v20160207/directives?query".
*
* If this is NULL, a "/" will be added to the Request-Line automatically.
*
* This is used to generate the Request-Line in the HTTP request message, see
* @ref https_client_function_initializerequest for more information.
*/
const char * pPath;
uint32_t pathLen; /**< @brief URI path length */
/**
* @brief On of the HTTP method tokens defined in #IotHttpsMethod_t.
*
* This is used to generate the Request-Line in the HTTP request message, see
* @ref https_client_function_initializerequest for more information.
*/
IotHttpsMethod_t method;
/**
* @brief Host address this request is intended for, e.g., "awsamazon.com".
*
* This is the same as the address in #IotHttpsConnectionInfo_t.pAddress. This is here in the request structure to
* automatically generate the "Host" header field in the header buffer space configured in
* #IotHttpsRequestInfo_t.userBuffer. See @ref https_client_function_initializerequest for more information.
*/
const char * pHost;
uint32_t hostLen; /**< @brief Host address length. */
/**
* @brief Flag denoting if the connection should be non-persistent.
*
* If this flag is set to false, then the connection is persistent. When the connection is persistent, the HTTP
* header "Connection: keep-alive" is automatically added to the headers to send to the server. This header
* asks the server to not close the connection after sending the response.
*
* If this flag is set to true, then the connection is non-persistent. When the connection is non-persistent, then
* HTTP header "Connection: close" is automatically added to the headers to send to the server. This header asks
* the server to close the connection after sending the response.
*
* Please see https://tools.ietf.org/html/rfc2616#section-8.1.1 for more details.
*/
bool isNonPersistent;
/**
* @brief Application owned buffer for storing the request headers and internal request context.
*
* For an asynchronous request, if the application owns the memory for this buffer, then it must not be modified,
* freed, or reused until the the #IotHttpsClientCallbacks_t.responseCompleteCallback is invoked.
*
* Please see #IotHttpsUserBuffer_t for more information.
*/
IotHttpsUserBuffer_t userBuffer;
/**
* @brief Indicator if this request is sync or async.
*
* Set this to false to use a synchronous request. Set this to true to use an asynchronous request.
*/
bool isAsync;
/**
* @brief Specific information for either a synchronous request or an asynchronous request.
*
* See #IotHttpsAsyncInfo_t for information on pAsyncInfo.
* See #IotHttpsSyncInfo_t for information on u.pSyncInfo.
*/
union
{
IotHttpsAsyncInfo_t * pAsyncInfo; /**< @brief Information specifically for Asynchronous requests. */
IotHttpsSyncInfo_t * pSyncInfo; /**< @brief Information specifically for synchronous requests. */
} u;
} IotHttpsRequestInfo_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief HTTP request configuration.
*
* @paramfor @ref https_client_function_sendsync and @ref https_client_function_sendasync
*
* A separate response info is defined so that the application can re-initialize a request for re-use while still
* processing a response that was already completed.
*/
typedef struct IotHttpsResponseInfo
{
/**
* The application owned buffer for storing the response headers and internal response context.
*
* For an asynchronous request, if the application owns the memory for this buffer, then it must not be modified,
* freed, or reused until the the #IotHttpsClientCallbacks_t.responseCompleteCallback is invoked.
*
* Please see #IotHttpsUserBuffer_t for more information.
*/
IotHttpsUserBuffer_t userBuffer;
/**
* @brief Specific information for a synchronously received response.
*
* Set this to NULL if the response is to be received asynchronously.
*
* See #IotHttpsSyncInfo_t for more information.
*/
IotHttpsSyncInfo_t * pSyncInfo;
} IotHttpsResponseInfo_t;
#endif /* ifndef IOT_HTTPS_TYPES_H_ */

View file

@ -0,0 +1,137 @@
/*
* Amazon FreeRTOS HTTPS Client V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_https_utils.c
* @brief Implements functions for HTTPS Client library utilities.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* iot_https_includes */
#include "iot_https_utils.h"
#include "http_parser.h"
#include "private/iot_https_internal.h"
/*-----------------------------------------------------------*/
IotHttpsReturnCode_t IotHttpsClient_GetUrlPath( const char * pUrl,
size_t urlLen,
const char ** pPath,
size_t * pPathLen )
{
/* http-parser status. Initialized to 0 to signify success. */
int parserStatus = 0;
struct http_parser_url urlParser;
IotHttpsReturnCode_t returnStatus = IOT_HTTPS_OK;
/* Sets all members in urlParser to 0. */
http_parser_url_init( &urlParser );
if( ( pUrl == NULL ) || ( pPath == NULL ) || ( pPathLen == NULL ) )
{
IotLogError( "NULL parameter passed to IotHttpsClient_GetUrlPath()." );
returnStatus = IOT_HTTPS_INVALID_PARAMETER;
}
if( returnStatus == IOT_HTTPS_OK )
{
parserStatus = http_parser_parse_url( pUrl, urlLen, 0, &urlParser );
if( parserStatus != 0 )
{
IotLogError( "Error parsing the input URL %.*s. Error code: %d.", urlLen, pUrl, parserStatus );
returnStatus = IOT_HTTPS_PARSING_ERROR;
}
}
if( returnStatus == IOT_HTTPS_OK )
{
*pPathLen = ( size_t ) ( urlParser.field_data[ UF_PATH ].len );
if( *pPathLen == 0 )
{
returnStatus = IOT_HTTPS_NOT_FOUND;
*pPath = NULL;
}
else
{
*pPath = &pUrl[ urlParser.field_data[ UF_PATH ].off ];
}
}
return returnStatus;
}
/*-----------------------------------------------------------*/
IotHttpsReturnCode_t IotHttpsClient_GetUrlAddress( const char * pUrl,
size_t urlLen,
const char ** pAddress,
size_t * pAddressLen )
{
/* http-parser status. Initialized to 0 to signify success. */
int parserStatus = 0;
struct http_parser_url urlParser;
IotHttpsReturnCode_t returnStatus = IOT_HTTPS_OK;
/* Sets all members in urlParser to 0. */
http_parser_url_init( &urlParser );
if( ( pUrl == NULL ) || ( pAddress == NULL ) || ( pAddressLen == NULL ) )
{
IotLogError( "NULL parameter passed to IotHttpsClient_GetUrlAddress()." );
returnStatus = IOT_HTTPS_INVALID_PARAMETER;
}
if( returnStatus == IOT_HTTPS_OK )
{
parserStatus = http_parser_parse_url( pUrl, urlLen, 0, &urlParser );
if( parserStatus != 0 )
{
IotLogError( "Error parsing the input URL %.*s. Error code: %d.", urlLen, pUrl, parserStatus );
returnStatus = IOT_HTTPS_PARSING_ERROR;
}
}
if( returnStatus == IOT_HTTPS_OK )
{
*pAddressLen = ( size_t ) ( urlParser.field_data[ UF_HOST ].len );
if( *pAddressLen == 0 )
{
returnStatus = IOT_HTTPS_NOT_FOUND;
*pAddress = NULL;
}
else
{
*pAddress = &pUrl[ urlParser.field_data[ UF_HOST ].off ];
}
}
return returnStatus;
}

View file

@ -0,0 +1,497 @@
/*
* Amazon FreeRTOS HTTPS Client V1.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
#ifndef IOT_HTTPS_INTERNAL_H_
#define IOT_HTTPS_INTERNAL_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard Includes. */
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
/* Third party http-parser include. */
#include "http_parser.h"
/* HTTPS Client library includes. */
#include "iot_https_client.h"
/* Task pool include. */
#include "iot_taskpool_freertos.h"
/* Linear containers (lists and queues) include. */
#include "iot_linear_containers.h"
/* Types include. */
#include "types/iot_taskpool_types_freertos.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
#include "platform/iot_network.h"
/* Error handling include. */
#include "iot_error.h"
/*-----------------------------------------------------------*/
/* Convenience macros for handling errors in a standard way. */
/**
* @brief Every public API return an enumeration value with an underlying value of 0 in case of success.
*/
#define HTTPS_SUCCEEDED( x ) ( ( x ) == IOT_HTTPS_OK )
/**
* @brief Every public API returns an enumeration value with an underlying value different than 0 in case of success.
*/
#define HTTPS_FAILED( x ) ( ( x ) != IOT_HTTPS_OK )
/**
* @brief Declare the storage for the error status variable.
*/
#define HTTPS_FUNCTION_ENTRY( result ) IOT_FUNCTION_ENTRY( IotHttpsReturnCode_t, result )
/**
* @brief Jump to the cleanup area.
*/
#define HTTPS_GOTO_CLEANUP() IOT_GOTO_CLEANUP()
/**
* @brief Set error and leave.
*/
#define HTTPS_SET_AND_GOTO_CLEANUP( statusValue ) IOT_SET_AND_GOTO_CLEANUP( statusValue )
/**
* @brief Initialize error and declare start of cleanup area.
*/
#define HTTPS_FUNCTION_CLEANUP_BEGIN() IOT_FUNCTION_CLEANUP_BEGIN()
/**
* @brief Initialize error and declare end of cleanup area.
*/
#define HTTPS_FUNCTION_CLEANUP_END() IOT_FUNCTION_CLEANUP_END()
/**
* @brief Create an empty cleanup area.
*/
#define HTTPS_FUNCTION_EXIT_NO_CLEANUP() IOT_FUNCTION_EXIT_NO_CLEANUP()
/**
* @brief Exit if an argument is NULL.
*/
#define HTTPS_ON_NULL_ARG_GOTO_CLEANUP( ptr ) \
if( ( ptr == NULL ) ) \
{ \
IotLogError( # ptr " was NULL." ); \
IOT_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INVALID_PARAMETER ); \
}
/**
* @brief Exit if an condition is false.
*/
#define HTTPS_ON_ARG_ERROR_GOTO_CLEANUP( expr ) \
if( ( expr ) == false ) \
{ \
IotLogError( # expr " must be true." ); \
IOT_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INVALID_PARAMETER ); \
}
/**
* @brief Exit if an argument is false with a message.
*/
#define HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( expr, statusValue, ... ) \
if( ( expr ) == false ) \
{ \
IotLogError( __VA_ARGS__ ); \
IOT_SET_AND_GOTO_CLEANUP( statusValue ); \
}
/* Configure logs for HTTPS Client functions. */
#ifdef IOT_LOG_LEVEL_HTTPS
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_HTTPS
#else
#ifdef IOT_LOG_LEVEL_GLOBAL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
#else
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
#endif
#endif
#define LIBRARY_LOG_NAME ( "HTTPS Client" )
#include "iot_logging_setup.h"
/*
* Provide default values for undefined memory allocation functions based on
* the usage of dynamic memory allocation.
*/
#if IOT_STATIC_MEMORY_ONLY == 1
#include "iot_static_memory.h"
#endif
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Provide default values for undefined configuration constants.
*/
#ifndef AWS_IOT_HTTPS_ENABLE_METRICS
#define AWS_IOT_HTTPS_ENABLE_METRICS ( 1 )
#endif
#ifndef IOT_HTTPS_USER_AGENT
#define IOT_HTTPS_USER_AGENT "FreeRTOS"
#endif
#ifndef IOT_HTTPS_MAX_FLUSH_BUFFER_SIZE
#define IOT_HTTPS_MAX_FLUSH_BUFFER_SIZE ( 1024 )
#endif
#ifndef IOT_HTTPS_RESPONSE_WAIT_MS
#define IOT_HTTPS_RESPONSE_WAIT_MS ( 1000 )
#endif
#ifndef IOT_HTTPS_MAX_HOST_NAME_LENGTH
#define IOT_HTTPS_MAX_HOST_NAME_LENGTH ( 255 ) /* Per FQDN, the maximum host name length is 255 bytes. */
#endif
#ifndef IOT_HTTPS_MAX_ALPN_PROTOCOLS_LENGTH
#define IOT_HTTPS_MAX_ALPN_PROTOCOLS_LENGTH ( 255 ) /* The maximum alpn protocols length is chosen arbitrarily. */
#endif
/** @endcond */
/**
* @brief The HTTP protocol version of this library is HTTP/1.1.
*/
#define HTTPS_PROTOCOL_VERSION "HTTP/1.1"
/**
* @brief An empty path for a NULL specified path in the request initialization configuration.
*/
#define HTTPS_EMPTY_PATH "/"
/**
* @brief HTTPS "CONNECT" method, defined as the longest string length method.
*/
#define HTTPS_CONNECT_METHOD "CONNECT"
/*
* Constants for the values of the HTTP "Connection" header field.
*
* This is used for writing headers automatically during the sending of the HTTP request.
* "Connection: keep-alive\r\n" is written automatically for a persistent connection.
* "Connection: close\r\n" is written automatically for a non-persistent connection.
*/
#define HTTPS_CONNECTION_KEEP_ALIVE_HEADER_VALUE "keep-alive"
#define HTTPS_CONNECTION_CLOSE_HEADER_VALUE "close"
/**
* Constants for HTTP header formatting.
*
* ": " separates and header field from the header value.
*/
#define HTTPS_HEADER_FIELD_SEPARATOR ": "
#define HTTPS_HEADER_FIELD_SEPARATOR_LENGTH ( 2 )
#define COLON_CHARACTER ':'
#define SPACE_CHARACTER ' '
/**
* Constants for HTTP header formatting.
*
* "\r\n" Ends the header line.
*/
#define HTTPS_END_OF_HEADER_LINES_INDICATOR "\r\n"
#define HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH ( 2 )
#define CARRIAGE_RETURN_CHARACTER '\r'
#define NEWLINE_CHARACTER '\n'
/*
* Constants for header fields added automatically during the request initialization.
*/
#define HTTPS_USER_AGENT_HEADER "User-Agent"
#define HTTPS_HOST_HEADER "Host"
/*
* Constants for the header fields added automatically during the sending of the HTTP request.
*/
#define HTTPS_CONTENT_LENGTH_HEADER "Content-Length"
#define HTTPS_CONNECTION_HEADER "Connection"
/**
* @brief The maximum Content-Length header line size.
*
* This is the length of header line string: "Content-Length: 4294967296\r\n". 4294967296 is 2^32. This number is chosen
* because it is the maximum file size that can be represented in a 32 bit system.
*
* This is used to initialize a local array for the final headers to send.
*/
#define HTTPS_MAX_CONTENT_LENGTH_LINE_LENGTH ( 26 )
/**
* @brief Macro for fast string length calculation of string macros.
*
* We subtract 1 to subtract the NULL terminating character.
* We do not assume that the size of a character is a single byte or 8 bits with this calculation.
*/
#define FAST_MACRO_STRLEN( x ) ( ( sizeof( x ) / sizeof( char ) ) - 1 )
/*-----------------------------------------------------------*/
/**
* @brief The state of the HTTP response parsing.
*
* This state notes what has been parsed in the HTTP response. As soon as any part of the HTTP response is received from
* the network, it is sent to be parsed.
*
* The states move as follows:
* PARSER_STATE_NONE --> PARSER_STATE_IN_HEADERS --> PARSER_STATE_HEADERS_COMPLETE --> PARSER_STATE_BODY_COMPLETE
*
* The parser callbacks are called in the following order:
* 1. _httpParserOnMessageBeginCallback()
* 2. _httpParserOnStatusCallback()
* 3. _httpParserOnHeaderFieldCallback()
* 4. _httpParserOnHeaderValueCallback()
* 5. _httpParserOnHeadersCompleteCallback()
* 6. _httpParserOnChunkHeaderCallback() (optional only if the response is chunked)
* 7. _httpParserOnBodyCallback()
* 8. _httpParserOnChunkCompleteCallback() (optional only if the response is chunked)
* 9. _httpParserOnMessageCompleteCallback()
*
* Theses states are set in the parser callbacks and used outside the callbacks to determine action.
*
* PARSER_STATE_NONE is assigned to #_httpsResponse_t.parserState when the _httpsResponse_t.parserState is initialized
* in @ref IotHttpsClient_InitializeRequest and before parsing a new respone message from the server.
*
* PARSER_STATE_IN_HEADERS is assigned at the start of the HTTP Response message. This occurs in the
* _httpParserOnMessageBeginCallback(). HTTP headers are always first and there is always the response status line
* and some headers in a response message according to RFC 2616.
*
* PARSER_STATE_HEADERS_COMPLETE is assigned when all of the headers are finished being parsed in the HTTP response
* message. This occurs in the _httpParserOnHeadersCompleteCallback(). The state can end here if the response has no
* body, like for a response to a HEAD request.
* If this state is not reached after receiving headers from the network into the user configured header buffer and
* running it through the parser, then we know that not all of the headers from the response could fit into the buffer.
*
* PARSER_STATE_IN_BODY is assigned each time the parser reaches HTTP response body. This occurs in the
* _httpParserOnBodyCallback().
*
* PARSER_STATE_BODY_COMPLETE is assigned when the parser has finished with the whole HTTP response message. This
* happens when _httpParserOnMessageCompleteCallback() is invoked.
* If this state is not reached after receiving body from the network into the user configured body buffer and
* running it through the parser, then we know that not all of the body from the response could fit into the buffer.
*/
typedef enum IotHttpsResponseParserState
{
PARSER_STATE_NONE = 0, /**< @brief The parser has not started so we are neither in the headers or the body. */
PARSER_STATE_IN_HEADERS, /**< @brief The parser is currently parsing the HTTP respone headers. */
PARSER_STATE_HEADERS_COMPLETE, /**< @brief The parser has finished parsing the headers. */
PARSER_STATE_IN_BODY, /**< @brief The parser is currently parsing the HTTP response body. */
PARSER_STATE_BODY_COMPLETE /**< @brief The parser has completed parsing the HTTP response body. */
} IotHttpsResponseParserState_t;
/**
* @brief The state denoting which buffer (the header buffer or the body buffer) is currently being processed
* and for what.
*
* This state is set outside of the parser callbacks and used inside the of parser callbacks to determine actions.
*
* The state moves as follows:
* Receiving and parsing a response: PROCESSING_STATE_NONE --> PROCESSING_STATE_FILLING_HEADER_BUFFER --> PROCESSING_STATE_FILLING_BODY_BUFFER --> PROCESSING_STATE_FINISHED
* Searching a response for headers: ((enter state)) --> PROCESSING_STATE_SEARCHING_HEADER_BUFFER --> ((enter state))
*
* PROCESSING_STATE_NONE is assigned when #_httpsResponse_t.bufferProcessingState is initialized in
* @ref IotHttpsClient_InitializeRequest.
*
* PROCESSING_STATE_FILLING_HEADER_BUFFER is assigned at the start of receiving HTTP response headers from the network
* into the header buffer, before processing the received headers with the parser.
* This state is then used in the parser callbacks _httpParserOnStatusCallback(), _httpParserOnHeaderFieldCallback(),
* _httpParserOnHeaderValueCallback(), and _httpParserOnHeadersCompleteCallback() to move the
* #_httpsResponse_t.headersCur pointer along in the header buffer.
* Since the server sends the HTTP response as a single continuous message, sometimes during receiving of the HTTP
* headers we may receive part or all of the HTTP response body:
* ((example header buffer))[headers headers headers headers body body body]
* When parsing this header buffer the parser will execute _httpParserOnBodyCallback() in the
* PROCESSING_STATE_FILLING_HEADER_BUFFER state. The state is used here, for an asynchronous response, to save where
* and how much body is inside the of the header buffer. When a body buffer becomes available, the body in the header
* buffer will be copied to the body buffer.
*
* PROCESSING_STATE_FILLING_BODY_BUFFER is assigned at the start of receiving the HTTP response body form the network
* into the body buffer, before processing the received body with the parser.
*
* PROCESSING_STATE_FINISHED is assigned at the end of IotHttpsClient_SendSync() or at the end of
* IotHttpsClient_SendAsync() when both the header and body buffer are finished being filled with network data and
* parsed.
*
* PROCESSING_STATE_SEARCHING_HEADER_BUFFER is assigned in IotHttpsClient_ReadHeader() when searching for a header
* in the header buffer.
* This state is used in the parser callback _httpParserOnHeaderFieldCallback() to check if the current header field
* parsed equals the header we are searching for. It is used in parser callback _httpParserOnHeaderValueCallback() to
* return the header value if the corresponding field we are searching for was found. It is used in parser callback
* _httpParserOnHeadersCompleteCallback() to stop parsing the header buffer if the header we are searching for was not
* found.
*
* The header buffer is separate from the body buffer.
* The header buffer is configured in #IotHttpRequestInfo_t.respUserBuff. The body buffer is configured in
* #IotHttpRequestInfo_t.syncInfo->respData or as buffer provided asynchronously during the
* #IotHttpsClientCallbacks_t.readReadyCallback() to call to @ref IotHttpsClient_ReadResponseBody().
*/
typedef enum IotHttpsResponseBufferState
{
PROCESSING_STATE_NONE, /**< @brief There is no buffer processing currently. */
PROCESSING_STATE_FILLING_HEADER_BUFFER, /**< @brief The header buffer is being filled and parsed. */
PROCESSING_STATE_FILLING_BODY_BUFFER, /**< @brief The body buffer is being filled and parsed. */
PROCESSING_STATE_FINISHED, /**< @brief Filling and parsing of both buffers is finished. */
PROCESSING_STATE_SEARCHING_HEADER_BUFFER /**< @brief The header buffer is being searched. */
} IotHttpsResponseBufferState_t;
/*-----------------------------------------------------------*/
/**
* @brief Represents an HTTP connection.
*/
typedef struct _httpsConnection
{
const IotNetworkInterface_t * pNetworkInterface; /**< @brief Network interface with calls for connect, disconnect, send, and receive. */
IotNetworkConnection_t pNetworkConnection; /**< @brief Pointer to the network connection to use pNetworkInterface calls on. */
uint32_t timeout; /**< @brief Timeout for a connection and waiting for a response from the network. */
/**
* @brief true if a connection was successful most recently on this context
*
* We have no way of knowing if the server closed the connection because that error is unique to the underlying TLS
* layer. This is set to false initially, then set to true for a successful intentional call to connect.
* Post connection, this is set to false only after an implicit disconnect with a non-persistent request, an implicit
* disconnect with a network error, or an explicit disconnect with a call to @ref https_client_function_disconnect.
*/
bool isConnected;
bool isDestroyed; /**< @brief true if the connection is already destroyed and we should call anymore */
IotMutex_t connectionMutex; /**< @brief Mutex protecting operations on this entire connection context. */
IotDeQueue_t reqQ; /**< @brief The queue for the requests that are not finished yet. */
IotDeQueue_t respQ; /**< @brief The queue for the responses that are waiting to be processed. */
IotTaskPoolJobStorage_t taskPoolJobStorage; /**< @brief An asynchronous operation requires storage for the task pool job. */
IotTaskPoolJob_t taskPoolJob; /**< @brief The task pool job identifier for an asynchronous request. */
} _httpsConnection_t;
/**
* @brief Third party library http-parser information.
*
* There are two separate structures for http_parser state information. This is so that the application can read
* a header during it's readReadyCallback. The readReadyCallback could be invoked many times and the parser will
* therefore be invoked many times for each response read from the network. In order to ensure that the state of
* the parser remains intact whilst headers may be read, two structures holding the state are kept.
*/
typedef struct _httpParserInfo
{
http_parser responseParser; /**< @brief http_parser state information for parsing the response. */
size_t ( * parseFunc )( http_parser * parser,
const http_parser_settings * settings,
const char * data,
size_t len ); /**< @brief http_parser_execute function is to be plugged in here during initialization of the response. */
http_parser readHeaderParser; /**< @brief http_parser state information for parsing the header buffer for reading a header. */
} _httpParserInfo_t;
/**
* @brief Represents an HTTP response.
*/
typedef struct _httpsResponse
{
IotLink_t link; /**< @brief The link to insert the job in the connection's respQ. */
uint8_t * pHeaders; /**< @brief Pointer to the start of the headers buffer. */
uint8_t * pHeadersEnd; /**< @brief Pointer to the end of the headers buffer. */
uint8_t * pHeadersCur; /**< @brief Pointer to the next location to write in the headers buffer. */
uint8_t * pBody; /**< @brief Pointer to the start of the body buffer. */
uint8_t * pBodyEnd; /**< @brief Pointer to the end of the body buffer. */
uint8_t * pBodyCur; /**< @brief Pointer to the next location to write in the body buffer. */
_httpParserInfo_t httpParserInfo; /**< @brief Third party http-parser information. */
uint16_t status; /**< @brief The HTTP response status code of this response. */
IotHttpsMethod_t method; /**< @brief The method of the originating request. */
IotHttpsResponseParserState_t parserState; /**< @brief The current state of the parser. See IotHttpsResponseParserState_t documentation for more details. */
IotHttpsResponseBufferState_t bufferProcessingState; /**< @brief Which buffer is currently being processed and for what. See IotHttpsResponseBufferState_t documentation. */
char * pReadHeaderField; /**< @brief Header field that we want to read from the headers buffer when IotHttpsClient_ReadHeader() is called. */
size_t readHeaderFieldLength; /**< @brief Length of pReadHeaderField */
char * pReadHeaderValue; /**< @brief Header value that we read from the headers buffer when IotHttpsClient_ReadHeader() is called. */
size_t readHeaderValueLength; /**< @brief Length of pReadHeaderValue. */
bool foundHeaderField; /**< @brief State to use during parsing to let us know when we found the header field in the https-parser callbacks.
* This is set to true when the header field is found in parser callback _httpParserOnHeaderFieldCallback().
* On the following parser callback _httpParserOnHeaderValueCallback() we will store the value in pReadHeaderValue and then exit the parsing. */
struct _httpsConnection * pHttpsConnection; /**< @brief Connection associated with response. This is set during IotHttpsClient_SendAsync(). This is needed during the asynchronous workflow to receive data given the respHandle only in the callback. */
bool isAsync; /**< @brief This is set to true if this response is to be retrieved asynchronously. Set to false otherwise. */
uint8_t * pBodyInHeaderBuf; /**< @brief Pointer to the start of body inside the header buffer for copying to a body buffer provided later by the asynchronous response process. */
uint8_t * pBodyCurInHeaderBuf; /**< @brief Pointer to the next location to write body data during processing of the header buffer. This is necessary in case there is a chunk encoded HTTP response. */
IotHttpsReturnCode_t bodyRxStatus; /**< @brief The status of network receiving the HTTPS body to be returned during the #IotHttpsClientCallbacks_t.readReadyCallback. */
bool cancelled; /**< @brief This is set to true to stop the request/response processing in the asynchronous request workflow. */
IotSemaphore_t respFinishedSem; /**< @brief This is for synchronous response to post that is finished being received. It is better to use a task event signal, but that is not implemented yet in the iot_threads.h API. */
IotHttpsReturnCode_t syncStatus; /**< @brief The status of the synchronous response. */
/**
* @brief This is set to true to when the request is finished being sent on the network
*
* A request is not shared with multiple tasks, so only one task will update this. This is to let the let the
* network receive callback know that the request is fully pushed out to the server. This is also to let the
* disconnect know that the request is not using the network interface resources anymore.
*/
bool reqFinishedSending;
IotHttpsClientCallbacks_t * pCallbacks; /**< @brief Pointer to the asynchronous request callbacks. */
void * pUserPrivData; /**< @brief User private data to hand back in the asynchronous callbacks for context. */
bool isNonPersistent; /**< @brief Non-persistent flag to indicate closing the connection immediately after receiving the response. */
} _httpsResponse_t;
/**
* @brief Represents and HTTP request.
*/
typedef struct _httpsRequest
{
IotLink_t link; /**< @brief The link to insert the job in the connection's reqQ. */
uint8_t * pHeaders; /**< @brief Pointer to the start of the headers buffer. */
uint8_t * pHeadersEnd; /**< @brief Pointer to the end of the headers buffer. */
uint8_t * pHeadersCur; /**< @brief Pointer to the next location to write in the headers buffer. */
uint8_t * pBody; /**< @brief Pointer to the start of the body buffer. */
uint32_t bodyLength; /**< @brief Length of request body buffer. */
IotHttpsMethod_t method; /**< @brief The method of the originating request. */
IotHttpsConnectionInfo_t * pConnInfo; /**< @brief Connection info associated with this request. For an implicit connection. */
struct _httpsResponse * pHttpsResponse; /**< @brief Response associated with request. This is initialized during IotHttpsClient_InitializeRequest(), then returned to the application in IotHttpsClient_SendAsync() and IotHttpsClient_SendSync(). */
struct _httpsConnection * pHttpsConnection; /**< @brief Connection associated with request. This is set during IotHttpsClient_SendAsync(). It is needed for the asynchronous workflow to use to send data given the reqHandle only in the callback. */
bool isNonPersistent; /**< @brief Non-persistent flag to indicate closing the connection immediately after receiving the response. */
bool isAsync; /**< @brief This is set to true if this request is to be sent asynchronously. Set to false otherwise. */
void * pUserPrivData; /**< @brief User private data to hand back in the asynchronous callbacks for context. */
IotHttpsClientCallbacks_t * pCallbacks; /**< @brief Pointer to the asynchronous request callbacks. */
bool cancelled; /**< @brief Set this to true to stop the response processing in the asynchronous workflow. */
IotHttpsReturnCode_t bodyTxStatus; /**< @brief The status of network sending the HTTPS body to be returned during the #IotHttpsClientCallbacks_t.writeCallback. */
bool scheduled; /**< @brief Set to true when this request has already been scheduled to the task pool. */
} _httpsRequest_t;
/*-----------------------------------------------------------*/
/**
* @brief A map of the method enum to strings
*
* These are in order to the HTTP request method enums defined in IotHttpsMethod_t.
*/
static const char * _pHttpsMethodStrings[] = {
"GET",
"HEAD",
"PUT",
"POST"
};
#endif /* IOT_HTTPS_INTERNAL_H_ */

View file

@ -0,0 +1,859 @@
/*
* IoT MQTT V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_mqtt.h
* @brief User-facing functions of the MQTT 3.1.1 library.
*/
#ifndef IOT_MQTT_H_
#define IOT_MQTT_H_
/* The config header is always included first. */
#include "iot_config.h"
/* MQTT types include. */
#include "types/iot_mqtt_types.h"
/*------------------------- MQTT library functions --------------------------*/
/**
* @functionspage{mqtt,MQTT library}
* - @functionname{mqtt_function_init}
* - @functionname{mqtt_function_cleanup}
* - @functionname{mqtt_function_receivecallback}
* - @functionname{mqtt_function_connect}
* - @functionname{mqtt_function_disconnect}
* - @functionname{mqtt_function_subscribeasync}
* - @functionname{mqtt_function_subscribesync}
* - @functionname{mqtt_function_unsubscribeasync}
* - @functionname{mqtt_function_unsubscribesync}
* - @functionname{mqtt_function_publishasync}
* - @functionname{mqtt_function_publishsync}
* - @functionname{mqtt_function_wait}
* - @functionname{mqtt_function_strerror}
* - @functionname{mqtt_function_operationtype}
* - @functionname{mqtt_function_issubscribed}
*/
/**
* @functionpage{IotMqtt_Init,mqtt,init}
* @functionpage{IotMqtt_Cleanup,mqtt,cleanup}
* @functionpage{IotMqtt_ReceiveCallback,mqtt,receivecallback}
* @functionpage{IotMqtt_Connect,mqtt,connect}
* @functionpage{IotMqtt_Disconnect,mqtt,disconnect}
* @functionpage{IotMqtt_SubscribeAsync,mqtt,subscribeasync}
* @functionpage{IotMqtt_SubscribeSync,mqtt,subscribesync}
* @functionpage{IotMqtt_UnsubscribeAsync,mqtt,unsubscribeasync}
* @functionpage{IotMqtt_UnsubscribeSync,mqtt,unsubscribesync}
* @functionpage{IotMqtt_PublishAsync,mqtt,publishasync}
* @functionpage{IotMqtt_PublishSync,mqtt,publishsync}
* @functionpage{IotMqtt_Wait,mqtt,wait}
* @functionpage{IotMqtt_strerror,mqtt,strerror}
* @functionpage{IotMqtt_OperationType,mqtt,operationtype}
* @functionpage{IotMqtt_IsSubscribed,mqtt,issubscribed}
*/
/**
* @brief One-time initialization function for the MQTT library.
*
* This function performs setup of the MQTT library. <b>It must be called
* once (and only once) before calling any other MQTT function.</b> Calling this
* function more than once without first calling @ref mqtt_function_cleanup
* may result in a crash.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
*
* @warning No thread-safety guarantees are provided for this function.
*
* @see @ref mqtt_function_cleanup
*/
/* @[declare_mqtt_init] */
IotMqttError_t IotMqtt_Init( void );
/* @[declare_mqtt_init] */
/**
* @brief One-time deinitialization function for the MQTT library.
*
* This function frees resources taken in @ref mqtt_function_init. It should be
* called after [closing all MQTT connections](@ref mqtt_function_disconnect) to
* clean up the MQTT library. After this function returns, @ref mqtt_function_init
* must be called again before calling any other MQTT function.
*
* @warning No thread-safety guarantees are provided for this function. Do not
* call this function if any MQTT connections are open!
*
* @see @ref mqtt_function_init
*/
/* @[declare_mqtt_cleanup] */
void IotMqtt_Cleanup( void );
/* @[declare_mqtt_cleanup] */
/**
* @brief Network receive callback for the MQTT library.
*
* This function should be called by the system whenever data is available for
* the MQTT library.
*
* @param[in] pNetworkConnection The network connection associated with the MQTT
* connection, passed by the network stack.
* @param[in] pReceiveContext A pointer to the MQTT connection handle for which
* the packet was received.
*/
/* @[declare_mqtt_receivecallback] */
void IotMqtt_ReceiveCallback( IotNetworkConnection_t pNetworkConnection,
void * pReceiveContext );
/* @[declare_mqtt_receivecallback] */
/**
* @brief Establish a new MQTT connection.
*
* This function opens a connection between a new MQTT client and an MQTT server
* (also called a <i>broker</i>). MQTT connections are established on top of transport
* layer protocols (such as TCP/IP), and optionally, application layer security
* protocols (such as TLS). The MQTT packet that establishes a connection is called
* the MQTT CONNECT packet. After @ref mqtt_function_init, this function must be
* called before any other MQTT library function.
*
* If [pConnectInfo->cleanSession](@ref IotMqttConnectInfo_t.cleanSession) is `true`,
* this function establishes a clean MQTT session. Subscriptions and unacknowledged
* PUBLISH messages will be discarded when the connection is closed.
*
* If [pConnectInfo->cleanSession](@ref IotMqttConnectInfo_t.cleanSession) is `false`,
* this function establishes (or re-establishes) a persistent MQTT session. The parameters
* [pConnectInfo->pPreviousSubscriptions](@ref IotMqttConnectInfo_t.pPreviousSubscriptions)
* and [pConnectInfo->previousSubscriptionCount](@ref IotMqttConnectInfo_t.previousSubscriptionCount)
* may be used to restore subscriptions present in a re-established persistent session.
* Any restored subscriptions <b>MUST</b> have been present in the persistent session;
* <b>this function does not send an MQTT SUBSCRIBE packet!</b>
*
* [pConnectInfo->pPreviousSubscriptions](@ref IotMqttConnectInfo_t.pPreviousSubscriptions)
* and [pConnectInfo->previousSubscriptionCount](@ref IotMqttConnectInfo_t.previousSubscriptionCount) can
* also be used to pass a list of subscriptions to be stored locally without a SUBSCRIBE packet being
* sent to the broker. These subscriptions are useful to invoke application level callbacks for messages received
* on unsolicited topics from the broker.
*
* This MQTT library is network agnostic, meaning it has no knowledge of the
* underlying network protocol carrying the MQTT packets. It interacts with the
* network through a network abstraction layer, allowing it to be used with many
* different network stacks. The network abstraction layer is established
* per-connection, allowing every #IotMqttConnection_t to use a different network
* stack. The parameter `pNetworkInterface` sets up the network abstraction layer
* for an MQTT connection; see the documentation on #IotMqttNetworkInfo_t for details
* on its members.
*
* The `pConnectInfo` parameter provides the contents of the MQTT CONNECT packet.
* Most members [are defined by the MQTT spec.](@ref IotMqttConnectInfo_t). The
* [pConnectInfo->pWillInfo](@ref IotMqttConnectInfo_t.pWillInfo) member provides
* information on a Last Will and Testament (LWT) message to be published if the
* MQTT connection is closed without [sending a DISCONNECT packet]
* (@ref mqtt_function_disconnect). Unlike other PUBLISH
* messages, a LWT message payload is limited to 65535 bytes in length. Additionally,
* the retry [interval](@ref IotMqttPublishInfo_t.retryMs) and [limit]
* (@ref IotMqttPublishInfo_t.retryLimit) members of #IotMqttPublishInfo_t
* are ignored for LWT messages. The LWT message is optional; `pWillInfo` may be NULL.
*
* Unlike @ref mqtt_function_publishasync, @ref mqtt_function_subscribeasync, and
* @ref mqtt_function_unsubscribeasync, this function is always blocking. Additionally,
* because the MQTT connection acknowledgement packet (CONNACK packet) does not
* contain any information on <i>which</i> CONNECT packet it acknowledges, only one
* CONNECT operation may be in progress at any time. This means that parallel
* threads making calls to @ref mqtt_function_connect will be serialized to send
* their CONNECT packets one-by-one.
*
* @param[in] pNetworkInfo Information on the transport-layer network connection
* to use with the MQTT connection.
* @param[in] pConnectInfo MQTT connection setup parameters.
* @param[in] timeoutMs If the MQTT server does not accept the connection within
* this timeout in milliseconds, this function returns #IOT_MQTT_TIMEOUT.
* @param[out] pMqttConnection Set to a newly-initialized MQTT connection handle
* if this function succeeds.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_TIMEOUT
* - #IOT_MQTT_SERVER_REFUSED
*
* <b>Example</b>
* @code{c}
*
* // Callback function to receive messages from the broker on an unsolicited topic.
* void unsolicitedMessageCallback( void * pArgument, IotMqttCallbackParam_t * pPublish );
*
* // Parameters to MQTT connect.
* IotMqttConnection_t mqttConnection = IOT_MQTT_CONNECTION_INITIALIZER;
* IotMqttNetworkInfo_t networkInfo = IOT_MQTT_NETWORK_INFO_INITIALIZER;
* IotMqttConnectInfo_t connectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER;
* IotMqttPublishInfo_t willInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
*
* // A local subscription to receive messages from the broker on an unsolicited topic.
* IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
*
* // Example network abstraction types.
* IotNetworkServerInfo_t serverInfo = { ... };
* IotNetworkCredentials_t credentialInfo = { ... };
* IotNetworkInterface_t networkInterface = { ... };
*
* // Example using a generic network implementation.
* networkInfo.createNetworkConnection = true;
* networkInfo.u.setup.pNetworkServerInfo = &serverInfo;
* networkInfo.u.setup.pNetworkCredentialInfo = &credentialInfo;
* networkInfo.pNetworkInterface = &networkInterface;
*
* // Set the members of the connection info (password and username not used).
* connectInfo.cleanSession = true;
* connectInfo.keepAliveSeconds = 30;
* connectInfo.pClientIdentifier = "uniqueclientidentifier";
* connectInfo.clientIdentifierLength = 22;
*
* // Set the members of the will info (retain and retry not used).
* willInfo.qos = IOT_MQTT_QOS_1;
* willInfo.pTopicName = "will/topic/name";
* willInfo.topicNameLength = ( uint16_t ) strlen( willInfo.pTopicName );
* willInfo.pPayload = "MQTT client unexpectedly disconnected.";
* willInfo.payloadLength = strlen( willInfo.pPayload );
*
* // Set the pointer to the will info.
* connectInfo.pWillInfo = &willInfo;
*
* // [Optional] Set a local subscription to receive the broker messages on an unsolicited topic.
* subscription.qos = IOT_MQTT_QOS_0;
* subscription.pTopicFilter = "some/unsolicited/topic";
* subscription.topicLength = ( uint16_t ) strlen( subscription.pTopicFilter );
* subscription.callback.function = unsolicitedMessageCallback;
* connectInfo.pPreviousSubscriptions = &subscription;
* connectInfo.previousSubscriptionCount = 1;
*
*
* // Call CONNECT with a 5 second block time. Should return
* // IOT_MQTT_SUCCESS when successful.
* IotMqttError_t result = IotMqtt_Connect( &networkInfo,
* &connectInfo,
* 5000,
* &mqttConnection );
*
* if( result == IOT_MQTT_SUCCESS )
* {
* // Do something with the MQTT connection...
*
* // Clean up and close the MQTT connection once it's no longer needed.
* IotMqtt_Disconnect( mqttConnection, 0 );
* }
* @endcode
*/
/* @[declare_mqtt_connect] */
IotMqttError_t IotMqtt_Connect( const IotMqttNetworkInfo_t * pNetworkInfo,
const IotMqttConnectInfo_t * pConnectInfo,
uint32_t timeoutMs,
IotMqttConnection_t * const pMqttConnection );
/* @[declare_mqtt_connect] */
/**
* @brief Closes an MQTT connection and frees resources.
*
* This function closes an MQTT connection and should only be called once
* the MQTT connection is no longer needed. Its exact behavior depends on the
* `flags` parameter.
*
* Normally, `flags` should be `0`. This gracefully shuts down an MQTT
* connection by sending an MQTT DISCONNECT packet. Any [network close function]
* (@ref IotNetworkInterface_t::close) provided [when the connection was established]
* (@ref mqtt_function_connect) will also be called. Note that because the MQTT server
* will not acknowledge a DISCONNECT packet, the client has no way of knowing if
* the server received the DISCONNECT packet. In the case where the DISCONNECT
* packet is lost in transport, any Last Will and Testament (LWT) message established
* with the connection may be published. However, if the DISCONNECT reaches the
* MQTT server, the LWT message will be discarded and not published.
*
* Should the underlying network connection become unusable, this function should
* be called with `flags` set to #IOT_MQTT_FLAG_CLEANUP_ONLY. In this case, no
* DISCONNECT packet will be sent, though the [network close function](@ref IotNetworkInterface_t::close)
* will still be called. This function will only free the resources used by the MQTT
* connection; it still must be called even if the network is offline to avoid leaking
* resources.
*
* @ref mqtt_function_disconnect modifies `mqttConnection`, so it shouldn't
* be used after calling this function.
*
* @param[in] mqttConnection The MQTT connection to close and clean up.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
*/
/* @[declare_mqtt_disconnect] */
void IotMqtt_Disconnect( IotMqttConnection_t mqttConnection,
uint32_t flags );
/* @[declare_mqtt_disconnect] */
/**
* @brief Subscribes to the given array of topic filters and optionally
* receive an asynchronous notification when the subscribe completes.
*
* This function sends an MQTT SUBSCRIBE packet to the server. A SUBSCRIBE
* packet notifies the server to send any matching PUBLISH messages to this client.
* A single SUBSCRIBE packet may carry more than one topic filter, hence the
* parameters to this function include an array of [subscriptions]
* (@ref IotMqttSubscription_t).
*
* An MQTT subscription has two pieces:
* 1. The subscription topic filter registered with the MQTT server. The MQTT
* SUBSCRIBE packet sent from this client to server notifies the server to send
* messages matching the given topic filters to this client.
* 2. The [callback function](@ref IotMqttCallbackInfo_t.function) that this
* client will invoke when an incoming message is received. The callback function
* notifies applications of an incoming PUBLISH message.
*
* The helper function @ref mqtt_function_issubscribed can be used to check if a
* [callback function](@ref IotMqttCallbackInfo_t.function) is registered for
* a particular topic filter.
*
* To modify an already-registered subscription callback, call this function with
* a new `pSubscriptionList`. Any topic filters in `pSubscriptionList` that already
* have a registered callback will be replaced with the new values in `pSubscriptionList`.
*
* @attention QoS 2 subscriptions are currently unsupported. Only 0 or 1 are valid
* for subscription QoS.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription.
* @param[in] pSubscriptionList Pointer to the first element in the array of
* subscriptions.
* @param[in] subscriptionCount The number of elements in pSubscriptionList.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion (`NULL` to disable).
* @param[out] pSubscribeOperation Set to a handle by which this operation may be
* referenced after this function returns. This reference is invalidated once
* the subscription operation completes.
*
* @return This function will return #IOT_MQTT_STATUS_PENDING upon success.
* @return Upon completion of the subscription (either through an
* #IotMqttCallbackInfo_t or @ref mqtt_function_wait), the status will be one of:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_SERVER_REFUSED
* @return If this function fails before queuing a subscribe operation, it will return
* one of:
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
*
* @see @ref mqtt_function_subscribesync for a blocking variant of this function.
* @see @ref mqtt_function_unsubscribeasync for the function that removes subscriptions.
*
* <b>Example</b>
* @code{c}
* #define NUMBER_OF_SUBSCRIPTIONS ...
*
* // Subscription callback function.
* void subscriptionCallback( void * pArgument, IotMqttCallbackParam_t * pPublish );
*
* // An initialized and connected MQTT connection.
* IotMqttConnection_t mqttConnection;
*
* // Subscription information.
* pSubscriptions[ NUMBER_OF_SUBSCRIPTIONS ] = { IOT_MQTT_SUBSCRIPTION_INITIALIZER };
* IotMqttOperation_t lastOperation = IOT_MQTT_OPERATION_INITIALIZER;
*
* // Set the subscription information.
* for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )
* {
* pSubscriptions[ i ].qos = IOT_MQTT_QOS_1;
* pSubscriptions[ i ].pTopicFilter = "some/topic/filter";
* pSubscriptions[ i ].topicLength = ( uint16_t ) strlen( pSubscriptions[ i ].pTopicFilter );
* pSubscriptions[ i ].callback.function = subscriptionCallback;
* }
*
* IotMqttError_t result = IotMqtt_SubscribeAsync( mqttConnection,
* pSubscriptions,
* NUMBER_OF_SUBSCRIPTIONS,
* IOT_MQTT_FLAG_WAITABLE,
* NULL,
* &lastOperation );
*
* // Subscribe returns IOT_MQTT_STATUS_PENDING when successful. Wait up to
* // 5 seconds for the operation to complete.
* if( result == IOT_MQTT_STATUS_PENDING )
* {
* result = IotMqtt_Wait( subscriptionRef, 5000 );
* }
*
* // Check that the subscriptions were successful.
* if( result == IOT_MQTT_SUCCESS )
* {
* // Wait for messages on the subscription topic filters...
*
* // Unsubscribe once the subscriptions are no longer needed.
* result = IotMqtt_UnsubscribeAsync( mqttConnection,
* pSubscriptions,
* NUMBER_OF_SUBSCRIPTIONS,
* IOT_MQTT_FLAG_WAITABLE,
* NULL,
* &lastOperation );
*
* // UNSUBSCRIBE returns IOT_MQTT_STATUS_PENDING when successful.
* // Wait up to 5 seconds for the operation to complete.
* if( result == IOT_MQTT_STATUS_PENDING )
* {
* result = IotMqtt_Wait( lastOperation, 5000 );
* }
* }
* // Check which subscriptions were rejected by the server.
* else if( result == IOT_MQTT_SERVER_REFUSED )
* {
* for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )
* {
* if( IotMqtt_IsSubscribed( mqttConnection,
* pSubscriptions[ i ].pTopicFilter,
* pSubscriptions[ i ].topicFilterLength,
* NULL ) == false )
* {
* // This subscription was rejected.
* }
* }
* }
* @endcode
*/
/* @[declare_mqtt_subscribeasync] */
IotMqttError_t IotMqtt_SubscribeAsync( IotMqttConnection_t mqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint32_t flags,
const IotMqttCallbackInfo_t * pCallbackInfo,
IotMqttOperation_t * const pSubscribeOperation );
/* @[declare_mqtt_subscribeasync] */
/**
* @brief Subscribes to the given array of topic filters with a timeout.
*
* This function sends an MQTT SUBSCRIBE packet to the server, then waits for
* a server response to the packet. Internally, this function is a call to @ref
* mqtt_function_subscribeasync followed by @ref mqtt_function_wait. See @ref
* mqtt_function_subscribeasync for more information about the MQTT SUBSCRIBE operation.
*
* @attention QoS 2 subscriptions are currently unsupported. Only 0 or 1 are valid
* for subscription QoS.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription.
* @param[in] pSubscriptionList Pointer to the first element in the array of
* subscriptions.
* @param[in] subscriptionCount The number of elements in pSubscriptionList.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* Currently, flags are ignored by this function; this parameter is for
* future-compatibility.
* @param[in] timeoutMs If the MQTT server does not acknowledge the subscriptions within
* this timeout in milliseconds, this function returns #IOT_MQTT_TIMEOUT.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_TIMEOUT
* - #IOT_MQTT_SERVER_REFUSED
*/
/* @[declare_mqtt_subscribesync] */
IotMqttError_t IotMqtt_SubscribeSync( IotMqttConnection_t mqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint32_t flags,
uint32_t timeoutMs );
/* @[declare_mqtt_subscribesync] */
/**
* @brief Unsubscribes from the given array of topic filters and optionally
* receive an asynchronous notification when the unsubscribe completes.
*
* This function sends an MQTT UNSUBSCRIBE packet to the server. An UNSUBSCRIBE
* packet removes registered topic filters from the server. After unsubscribing,
* the server will no longer send messages on these topic filters to the client.
*
* Corresponding [subscription callback functions](@ref IotMqttCallbackInfo_t.function)
* are also removed from the MQTT connection. These subscription callback functions
* will be removed even if the MQTT UNSUBSCRIBE packet fails to send.
*
* @param[in] mqttConnection The MQTT connection used for the subscription.
* @param[in] pSubscriptionList Pointer to the first element in the array of
* subscriptions.
* @param[in] subscriptionCount The number of elements in pSubscriptionList.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion (`NULL` to disable).
* @param[out] pUnsubscribeOperation Set to a handle by which this operation may be
* referenced after this function returns. This reference is invalidated once
* the unsubscribe operation completes.
*
* @return This function will return #IOT_MQTT_STATUS_PENDING upon success.
* @return Upon completion of the unsubscribe (either through an
* #IotMqttCallbackInfo_t or @ref mqtt_function_wait), the status will be one of:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* @return If this function fails before queuing an unsubscribe operation, it will return
* one of:
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
*
* @see @ref mqtt_function_unsubscribesync for a blocking variant of this function.
* @see @ref mqtt_function_subscribeasync for the function that adds subscriptions.
*/
/* @[declare_mqtt_unsubscribeasync] */
IotMqttError_t IotMqtt_UnsubscribeAsync( IotMqttConnection_t mqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint32_t flags,
const IotMqttCallbackInfo_t * pCallbackInfo,
IotMqttOperation_t * const pUnsubscribeOperation );
/* @[declare_mqtt_unsubscribeasync] */
/**
* @brief Unsubscribes from a given array of topic filters with a timeout.
*
* This function sends an MQTT UNSUBSCRIBE packet to the server, then waits
* for a server response to the packet. Internally, this function is a call to
* @ref mqtt_function_unsubscribeasync followed by @ref mqtt_function_wait. See @ref
* mqtt_function_unsubscribeasync for more information about the MQTT UNSUBSCRIBE
* operation.
*
* @param[in] mqttConnection The MQTT connection used for the subscription.
* @param[in] pSubscriptionList Pointer to the first element in the array of
* subscriptions.
* @param[in] subscriptionCount The number of elements in pSubscriptionList.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* Flags are currently ignored but reserved for future use.
* @param[in] timeoutMs If the MQTT server does not acknowledge the UNSUBSCRIBE within
* this timeout in milliseconds, this function returns #IOT_MQTT_TIMEOUT.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
*/
/* @[declare_mqtt_unsubscribesync] */
IotMqttError_t IotMqtt_UnsubscribeSync( IotMqttConnection_t mqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint32_t flags,
uint32_t timeoutMs );
/* @[declare_mqtt_unsubscribesync] */
/**
* @brief Publishes a message to the given topic name and optionally
* receive an asynchronous notification when the publish completes.
*
* This function sends an MQTT PUBLISH packet to the server. A PUBLISH packet
* contains a payload and a topic name. Any clients with a subscription on a
* topic filter matching the PUBLISH topic name will receive a copy of the
* PUBLISH packet from the server.
*
* If a PUBLISH packet fails to reach the server and it is not a QoS 0 message,
* it will be retransmitted. See #IotMqttPublishInfo_t for a description
* of the retransmission strategy.
*
* @attention QoS 2 messages are currently unsupported. Only 0 or 1 are valid
* for message QoS.
*
* @param[in] mqttConnection The MQTT connection to use for the publish.
* @param[in] pPublishInfo MQTT publish parameters.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion (`NULL` to disable).
* @param[out] pPublishOperation Set to a handle by which this operation may be
* referenced after this function returns. This reference is invalidated once
* the publish operation completes.
*
* @return This function will return #IOT_MQTT_STATUS_PENDING upon success for
* QoS 1 publishes. For a QoS 0 publish it returns #IOT_MQTT_SUCCESS upon
* success.
* @return Upon completion of a QoS 1 publish (either through an
* #IotMqttCallbackInfo_t or @ref mqtt_function_wait), the status will be one of:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_RETRY_NO_RESPONSE (if [pPublishInfo->retryMs](@ref IotMqttPublishInfo_t.retryMs)
* and [pPublishInfo->retryLimit](@ref IotMqttPublishInfo_t.retryLimit) were set).
* @return If this function fails before queuing an publish operation (regardless
* of QoS), it will return one of:
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
*
* @note The parameters `pCallbackInfo` and `pPublishOperation` should only be used for QoS
* 1 publishes. For QoS 0, they should both be `NULL`.
*
* @see @ref mqtt_function_publishsync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* // An initialized and connected MQTT connection.
* IotMqttConnection_t mqttConnection;
*
* // Publish information.
* IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
*
* // Set the publish information. QoS 0 example (retain not used):
* publishInfo.qos = IOT_MQTT_QOS_0;
* publishInfo.pTopicName = "some/topic/name";
* publishInfo.topicNameLength = ( uint16_t ) strlen( publishInfo.pTopicName );
* publishInfo.pPayload = "payload";
* publishInfo.payloadLength = strlen( publishInfo.pPayload );
*
* // QoS 0 publish should return IOT_MQTT_SUCCESS upon success.
* IotMqttError_t qos0Result = IotMqtt_PublishAsync( mqttConnection,
* &publishInfo,
* 0,
* NULL,
* NULL );
*
* // QoS 1 with retry example (using same topic name and payload as QoS 0 example):
* IotMqttOperation_t qos1Operation = IOT_MQTT_OPERATION_INITIALIZER;
* publishInfo.qos = IOT_MQTT_QOS_1;
* publishInfo.retryMs = 1000; // Retry if no response is received in 1 second.
* publishInfo.retryLimit = 5; // Retry up to 5 times.
*
* // QoS 1 publish should return IOT_MQTT_STATUS_PENDING upon success.
* IotMqttError_t qos1Result = IotMqtt_PublishAsync( mqttConnection,
* &publishInfo,
* IOT_MQTT_FLAG_WAITABLE,
* NULL,
* &qos1Operation );
*
* // Wait up to 5 seconds for the publish to complete.
* if( qos1Result == IOT_MQTT_STATUS_PENDING )
* {
* qos1Result = IotMqtt_Wait( qos1Operation, 5000 );
* }
* @endcode
*/
/* @[declare_mqtt_publishasync] */
IotMqttError_t IotMqtt_PublishAsync( IotMqttConnection_t mqttConnection,
const IotMqttPublishInfo_t * pPublishInfo,
uint32_t flags,
const IotMqttCallbackInfo_t * pCallbackInfo,
IotMqttOperation_t * const pPublishOperation );
/* @[declare_mqtt_publishasync] */
/**
* @brief Publish a message to the given topic name with a timeout.
*
* This function sends an MQTT PUBLISH packet to the server, then waits for
* a server response to the packet. Internally, this function is a call to @ref
* mqtt_function_publishasync followed by @ref mqtt_function_wait. See @ref
* mqtt_function_publishasync for more information about the MQTT PUBLISH operation.
*
* @attention QoS 2 messages are currently unsupported. Only 0 or 1 are valid
* for message QoS.
*
* @param[in] mqttConnection The MQTT connection to use for the publish.
* @param[in] pPublishInfo MQTT publish parameters.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* Currently, flags are ignored by this function; this parameter is for
* future-compatibility.
* @param[in] timeoutMs If the MQTT server does not acknowledge a QoS 1 PUBLISH
* within this timeout in milliseconds, this function returns #IOT_MQTT_TIMEOUT.
* This parameter is ignored for QoS 0 PUBLISH messages.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_RETRY_NO_RESPONSE (if [pPublishInfo->retryMs](@ref IotMqttPublishInfo_t.retryMs)
* and [pPublishInfo->retryLimit](@ref IotMqttPublishInfo_t.retryLimit) were set).
*/
/* @[declare_mqtt_publishsync] */
IotMqttError_t IotMqtt_PublishSync( IotMqttConnection_t mqttConnection,
const IotMqttPublishInfo_t * pPublishInfo,
uint32_t flags,
uint32_t timeoutMs );
/* @[declare_mqtt_publishsync] */
/**
* @brief Waits for an operation to complete.
*
* This function blocks to wait for a [subscribe](@ref mqtt_function_subscribeasync),
* [unsubscribe](@ref mqtt_function_unsubscribeasync), or [publish]
* (@ref mqtt_function_publishasync) to complete. These operations are by default
* asynchronous; the function calls queue an operation for processing, and a
* callback is invoked once the operation is complete.
*
* To use this function, the flag #IOT_MQTT_FLAG_WAITABLE must have been
* set in the operation's function call. Additionally, this function must always
* be called with any waitable operation to clean up resources.
*
* Regardless of its return value, this function always clean up resources used
* by the waitable operation. This means `reference` is invalidated as soon as
* this function returns, even if it returns #IOT_MQTT_TIMEOUT or another error.
*
* @param[in] operation Reference to the operation to wait for. The flag
* #IOT_MQTT_FLAG_WAITABLE must have been set for this operation.
* @param[in] timeoutMs How many milliseconds to wait before returning
* #IOT_MQTT_TIMEOUT.
*
* @return The return value of this function depends on the MQTT operation associated
* with `reference`. See #IotMqttError_t for possible return values.
*
* <b>Example</b>
* @code{c}
* // Operation reference and timeout.
* IotMqttOperation_t publishOperation = IOT_MQTT_OPERATION_INITIALIZER;
* uint32_t timeoutMs = 5000; // 5 seconds
*
* // MQTT operation to wait for.
* IotMqttError_t result = IotMqtt_PublishAsync( mqttConnection,
* &publishInfo,
* IOT_MQTT_FLAG_WAITABLE,
* NULL,
* &publishOperation );
*
* // Publish should have returned IOT_MQTT_STATUS_PENDING. The call to wait
* // returns once the result of the publish is available or the timeout expires.
* if( result == IOT_MQTT_STATUS_PENDING )
* {
* result = IotMqtt_Wait( publishOperation, timeoutMs );
*
* // After the call to wait, the result of the publish is known
* // (not IOT_MQTT_STATUS_PENDING).
* assert( result != IOT_MQTT_STATUS_PENDING );
* }
* @endcode
*/
/* @[declare_mqtt_wait] */
IotMqttError_t IotMqtt_Wait( IotMqttOperation_t operation,
uint32_t timeoutMs );
/* @[declare_mqtt_wait] */
/*-------------------------- MQTT helper functions --------------------------*/
/**
* @brief Returns a string that describes an #IotMqttError_t.
*
* Like the POSIX `strerror`, this function returns a string describing a
* return code. In this case, the return code is an MQTT library error code,
* `status`.
*
* The string returned by this function <b>MUST</b> be treated as read-only: any
* attempt to modify its contents may result in a crash. Therefore, this function
* is limited to usage in logging.
*
* @param[in] status The status to describe.
*
* @return A read-only string that describes `status`.
*
* @warning The string returned by this function must never be modified.
*/
/* @[declare_mqtt_strerror] */
const char * IotMqtt_strerror( IotMqttError_t status );
/* @[declare_mqtt_strerror] */
/**
* @brief Returns a string that describes an #IotMqttOperationType_t.
*
* This function returns a string describing an MQTT operation type, `operation`.
*
* The string returned by this function <b>MUST</b> be treated as read-only: any
* attempt to modify its contents may result in a crash. Therefore, this function
* is limited to usage in logging.
*
* @param[in] operation The operation to describe.
*
* @return A read-only string that describes `operation`.
*
* @warning The string returned by this function must never be modified.
*/
/* @[declare_mqtt_operationtype] */
const char * IotMqtt_OperationType( IotMqttOperationType_t operation );
/* @[declare_mqtt_operationtype] */
/**
* @brief Check if an MQTT connection has a subscription for a topic filter.
*
* This function checks whether an MQTT connection `mqttConnection` has a
* subscription callback registered for a topic filter `pTopicFilter`. If a
* subscription callback is found, its details are copied into the output parameter
* `pCurrentSubscription`. This subscription callback will be invoked for incoming
* PUBLISH messages on `pTopicFilter`.
*
* <b>The check for a matching subscription is only performed client-side</b>;
* therefore, this function should not be relied upon for perfect accuracy. For
* example, this function may return an incorrect result if the MQTT server
* crashes and drops subscriptions without informing the client.
*
* Note that an MQTT connection's subscriptions might change between the time this
* function checks the subscription list and its caller tests the return value.
* This function certainly should not be used concurrently with any pending SUBSCRIBE
* or UNSUBSCRIBE operations.
*
* One suitable use of this function is to check <i>which</i> subscriptions were rejected
* if @ref mqtt_function_subscribeasync returns #IOT_MQTT_SERVER_REFUSED; that return
* code only means that <i>at least one</i> subscription was rejected.
*
* @param[in] mqttConnection The MQTT connection to check.
* @param[in] pTopicFilter The topic filter to check.
* @param[in] topicFilterLength Length of `pTopicFilter`.
* @param[out] pCurrentSubscription If a subscription is found, its details are
* copied here. This output parameter is only valid if this function returns `true` (`NULL` to disable).
*
* @return `true` if a subscription was found; `false` otherwise.
*
* @note The subscription QoS is not stored by the MQTT library; therefore,
* `pCurrentSubscription->qos` will always be set to #IOT_MQTT_QOS_0.
*/
/* @[declare_mqtt_issubscribed] */
bool IotMqtt_IsSubscribed( IotMqttConnection_t mqttConnection,
const char * pTopicFilter,
uint16_t topicFilterLength,
IotMqttSubscription_t * const pCurrentSubscription );
/* @[declare_mqtt_issubscribed] */
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Backwards compatibility macros for previous function names.
*/
#define IotMqtt_Subscribe IotMqtt_SubscribeAsync
#define IotMqtt_TimedSubscribe IotMqtt_SubscribeSync
#define IotMqtt_Unsubscribe IotMqtt_UnsubscribeAsync
#define IotMqtt_TimedUnsubscribe IotMqtt_UnsubscribeSync
#define IotMqtt_Publish IotMqtt_PublishAsync
#define IotMqtt_TimedPublish IotMqtt_PublishSync
/** @endcond */
#endif /* ifndef IOT_MQTT_H_ */

View file

@ -0,0 +1,702 @@
/*
* IoT MQTT V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_mqtt_serialize.h
* @brief User-facing functions for serializing MQTT 3.1.1 packets. This header should
* be included for building a single threaded light-weight MQTT client bypassing
* stateful CSDK MQTT library.
*/
#ifndef _IOT_MQTT_SERIALIZE_H_
#define _IOT_MQTT_SERIALIZE_H_
/* The config header is always included first. */
#include "iot_config.h"
/* MQTT types include. */
#include "types/iot_mqtt_types.h"
/*------------------------- MQTT library functions --------------------------*/
/**
* @functionspage{mqtt,MQTT library}
* - @functionname{mqtt_function_getconnectpacketsize}
* - @functionname{mqtt_function_serializeconnect}
* - @functionname{mqtt_function_getsubscriptionpacketsize}
* - @functionname{mqtt_function_serializesubscribe}
* - @functionname{mqtt_function_serializeunsubscribe}
* - @functionname{mqtt_function_getpublishpacketsize}
* - @functionname{mqtt_function_serializepublish}
* - @functionname{mqtt_function_serializedisconnect}
* - @functionname{mqtt_function_serializepingreq}
* - @functionname{mqtt_function_getincomingmqttpackettypeandlength}
* - @functionname{mqtt_function_deserializeresponse}
* - @functionname{mqtt_function_deserializepublish}
*/
/**
* @functionpage{IotMqtt_GetConnectPacketSize,mqtt,getconnectpacketsize}
* @functionpage{IotMqtt_SerializeConnect,mqtt,serializeconnect}
* @functionpage{IotMqtt_GetSubscriptionPacketSize,mqtt,getsubscriptionpacketsize}
* @functionpage{IotMqtt_SerializeSubscribe,mqtt,serializesubscribe}
* @functionpage{IotMqtt_SerializeUnsubscribe,mqtt,serializeunsubscribe}
* @functionpage{IotMqtt_GetPublishPacketSize,mqtt,getpublishpacketsize}
* @functionpage{IotMqtt_SerializePublish,mqtt,serializepublish}
* @functionpage{IotMqtt_SerializeDisconnect,mqtt,serializedisconnect}
* @functionpage{IotMqtt_SerializePingreq,mqtt,serializepingreq}
* @functionpage{IotMqtt_GetIncomingMQTTPacketTypeAndLength,mqtt,getincomingmqttpackettypeandlength}
* @functionpage{IotMqtt_DeserializeResponse,mqtt,deserializeresponse}
* @functionpage{IotMqtt_DeserializePublish,mqtt,deserializepublish}
*/
/**
* @brief Calculate the size and "Remaining length" of a CONNECT packet generated
* from the given parameters.
*
* @param[in] pConnectInfo User-provided CONNECT information struct.
* @param[out] pRemainingLength Output for calculated "Remaining length" field.
* @param[out] pPacketSize Output for calculated total packet size.
*
* @return IOT_MQTT_SUCCESS if the packet is within the length allowed by MQTT 3.1.1 spec;
* IOT_MQTT_BAD_PARAMETER otherwise. If this function returns `IOT_MQTT_BAD_PARAMETER`,
* the output parameters should be ignored.
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_GetConnectPacketSize() should be used to calculate
* // the size of connect request.
*
* IotMqttConnectInfo_t xConnectInfo;
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
* IotMqttError_t xResult;
*
* // start with everything set to zero
* memset( ( void * ) &xConnectInfo, 0x00, sizeof( xConnectInfo ) );
*
* // Initialize connection info, details are out of scope for this example.
* _initializeConnectInfo( &xConnectInfo );
* // Get size requirement for the connect packet
* xResult = IotMqtt_GetConnectPacketSize( &xConnectInfo, &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // Application should allocate buffer with size == xPacketSize or use static buffer
* // with size >= xPacketSize to serialize connect request.
* @endcode
*/
/* @[declare_mqtt_getconnectpacketsize] */
IotMqttError_t IotMqtt_GetConnectPacketSize( const IotMqttConnectInfo_t * pConnectInfo,
size_t * pRemainingLength,
size_t * pPacketSize );
/* @[declare_mqtt_getconnectpacketsize] */
/**
* @brief Generate a CONNECT packet from the given parameters.
*
* @param[in] pConnectInfo User-provided CONNECT information.
* @param[in] remainingLength remaining length of the packet to be serialized.
* @param[in, out] pBuffer User provided buffer where the CONNECT packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.
*
* @note pBuffer must be allocated by caller. Use @ref mqtt_function_getconnectpacketsize
* to determine the required size.
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_SerializeConnect() should be used to serialize
* // MQTT connect packet and send it to MQTT broker.
* // Example uses static memory but dynamically allocated memory can be used as well.
* // Get size requirement for the connect packet.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendConnectPacket( int xMQTTSocket )
* {
* IotMqttConnectInfo_t xConnectInfo;
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
* IotMqttError_t xResult;
* size_t xSentBytes = 0;
* // Get size requirement for MQTT connect packet.
* xResult = IotMqtt_GetConnectPacketSize( &xConnectInfo, &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // Make sure the packet size is less than static buffer size
* IotMqtt_Assert( xPacketSize < mqttexampleSHARED_BUFFER_SIZE );
* // Serialize MQTT connect packet into provided buffer
* xResult = IotMqtt_SerializeConnect( &xConnectInfo, xRemainingLength, ucSharedBuffer, xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentBytes = send( xMQTTSocket, ( void * ) ucSharedBuffer, xPacketSize, 0 );
* IotMqtt_Assert( xSentBytes == xPacketSize );
* }
* @endcode
*/
/* @[declare_mqtt_serializeconnect] */
IotMqttError_t IotMqtt_SerializeConnect( const IotMqttConnectInfo_t * pConnectInfo,
size_t remainingLength,
uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializeconnect] */
/**
* @brief Calculate the size and "Remaining length" of a SUBSCRIBE or UNSUBSCRIBE
* packet generated from the given parameters.
*
* @param[in] type Either IOT_MQTT_SUBSCRIBE or IOT_MQTT_UNSUBSCRIBE.
* @param[in] pSubscriptionList User-provided array of subscriptions.
* @param[in] subscriptionCount Size of `pSubscriptionList`.
* @param[out] pRemainingLength Output for calculated "Remaining length" field.
* @param[out] pPacketSize Output for calculated total packet size.
*
* @return IOT_MQTT_SUCCESS if the packet is within the length allowed by MQTT 3.1.1 spec;
* IOT_MQTT_BAD_PARAMETER otherwise. If this function returns IOT_MQTT_BAD_PARAMETER,
* the output parameters should be ignored.
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_GetSubscriptionPacketSize() should be used to calculate
* // the size of subscribe or unsubscribe request.
*
* IotMqttError_t xResult;
* IotMqttSubscription_t xMQTTSubscription[ 1 ];
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
*
* // Initialize Subscribe parameters. Details are out of scope for this example.
* // It will involve setting QOS, topic filter and topic filter length.
* _initializeSubscribe( xMQTTSubscription );
*
* xResult = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
* xMQTTSubscription,
* sizeof( xMQTTSubscription ) / sizeof( IotMqttSubscription_t ),
* &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // Application should allocate buffer with size == xPacketSize or use static buffer
* // with size >= xPacketSize to serialize connect request.
* @endcode
*/
/* @[declare_mqtt_getsubscriptionpacketsize] */
IotMqttError_t IotMqtt_GetSubscriptionPacketSize( IotMqttOperationType_t type,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t * pRemainingLength,
size_t * pPacketSize );
/* @[declare_mqtt_getsubscriptionpacketsize] */
/**
* @brief Generate a SUBSCRIBE packet from the given parameters.
*
* @param[in] pSubscriptionList User-provided array of subscriptions.
* @param[in] subscriptionCount Size of `pSubscriptionList`.
* @param[in] remainingLength remaining length of the packet to be serialized.
* @param[out] pPacketIdentifier The packet identifier generated for this SUBSCRIBE.
* @param[in, out] pBuffer User provide buffer where the SUBSCRIBE packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.
*
* @note pBuffer must be allocated by caller.
* @note This call is part of serializer API used for implementing light-weight MQTT client.
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_SerializeSubscribe() should be used to serialize
* // MQTT Subscribe packet and send it to MQTT broker.
* // Example uses static memory, but dynamically allocated memory can be used as well.
* // Get size requirement for the MQTT subscribe packet.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendSubscribePacket( int xMQTTSocket )
* {
* IotMqttSubscription_t xMQTTSubscription[ 1 ];
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
* IotMqttError_t xResult;
* size_t xSentBytes = 0;
*
* // Initialize Subscribe parameters. Details are out of scope for this example.
* // It will involve setting QOS, topic filter and topic filter length.
* _initializeSubscribe( xMQTTSubscription );
* // Get size requirement for MQTT Subscribe packet.
* xResult = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
* xMQTTSubscription,
* sizeof( xMQTTSubscription ) / sizeof( IotMqttSubscription_t ),
* &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // Make sure the packet size is less than static buffer size.
* IotMqtt_Assert( xPacketSize < mqttexampleSHARED_BUFFER_SIZE );
*
* // Serialize subscribe into statically allocated ucSharedBuffer.
* xResult = IotMqtt_SerializeSubscribe( xMQTTSubscription,
* sizeof( xMQTTSubscription ) / sizeof( IotMqttSubscription_t ),
* xRemainingLength,
* &usPacketIdentifier,
* ucSharedBuffer,
* xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentBytes = send( xMQTTSocket, ( void * ) ucSharedBuffer, xPacketSize, 0 );
* IotMqtt_Assert( xSentBytes == xPacketSize );
* }
* @endcode
*/
/* @[declare_mqtt_serializesubscribe] */
IotMqttError_t IotMqtt_SerializeSubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializesubscribe] */
/**
* @brief Generate a UNSUBSCRIBE packet from the given parameters.
*
* @param[in] pSubscriptionList User-provided array of subscriptions to remove.
* @param[in] subscriptionCount Size of `pSubscriptionList`.
* @param[in] remainingLength remaining length of the packet to be serialized.
* @param[out] pPacketIdentifier The packet identifier generated for this UNSUBSCRIBE.
* @param[in, out] pBuffer User provide buffer where the UNSUBSCRIBE packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.
*
* @note pBuffer must be allocated by caller.
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_SerializeUnsubscribe() should be used to serialize
* // MQTT unsubscribe packet and send it to MQTT broker.
* // Example uses static memory, but dynamically allocated memory can be used as well.
* // Get size requirement for the Unsubscribe packet.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendUnsubscribePacket( int xMQTTSocket )
* {
* // Following example shows one topic example.
* IotMqttSubscription_t xMQTTSubscription[ 1 ];
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
* IotMqttError_t xResult;
* size_t xSentBytes = 0;
* // Get size requirement for MQTT unsubscribe packet.
* xResult = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_UNSUBSCRIBE,
* xMQTTSubscription,
* sizeof( xMQTTSubscription ) / sizeof( IotMqttSubscription_t ),
* &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // Make sure the packet size is less than static buffer size.
* IotMqtt_Assert( xPacketSize < mqttexampleSHARED_BUFFER_SIZE );
* // Serialize subscribe into statically allocated ucSharedBuffer.
* xResult = IotMqtt_SerializeUnsubscribe( xMQTTSubscription,
* sizeof( xMQTTSubscription ) / sizeof( IotMqttSubscription_t ),
* xRemainingLength,
* &usPacketIdentifier,
* ucSharedBuffer,
* xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentBytes = send( xMQTTSocket, ( void * ) ucSharedBuffer, xPacketSize, 0 );
* IotMqtt_Assert( xSentBytes == xPacketSize );
* }
* @endcode
*/
/* @[declare_mqtt_serializeunsubscribe] */
IotMqttError_t IotMqtt_SerializeUnsubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializeunsubscribe] */
/**
* @brief Calculate the size and "Remaining length" of a PUBLISH packet generated
* from the given parameters.
*
* @param[in] pPublishInfo User-provided PUBLISH information struct.
* @param[out] pRemainingLength Output for calculated "Remaining length" field.
* @param[out] pPacketSize Output for calculated total packet size.
*
* @return IOT_MQTT_SUCCESS if the packet is within the length allowed by MQTT 3.1.1 spec;
* IOT_MQTT_BAD_PARAMETER otherwise. If this function returns IOT_MQTT_BAD_PARAMETER,
* the output parameters should be ignored.
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_GetPublishPacketSize() should be used to calculate
* // the size of MQTT publish request.
*
* IotMqttError_t xResult;
* IotMqttPublishInfo_t xMQTTPublishInfo;
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
*
* // Initialize Publish parameters. Details are out of scope for this example.
* // It will involve setting QOS, topic filter, topic filter length, payload
* // payload length
* _initializePublish( &xMQTTPublishInfo );
*
* // Find out length of Publish packet size.
* xResult = IotMqtt_GetPublishPacketSize( &xMQTTPublishInfo, &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // Application should allocate buffer with size == xPacketSize or use static buffer
* // with size >= xPacketSize to serialize connect request.
* @endcode
*/
/* @[declare_mqtt_getpublishpacketsize] */
IotMqttError_t IotMqtt_GetPublishPacketSize( IotMqttPublishInfo_t * pPublishInfo,
size_t * pRemainingLength,
size_t * pPacketSize );
/* @[declare_mqtt_getpublishpacketsize] */
/**
* @brief Generate a PUBLISH packet from the given parameters.
*
* @param[in] pPublishInfo User-provided PUBLISH information.
* @param[in] remainingLength remaining length of the packet to be serialized.
* @param[out] pPacketIdentifier The packet identifier generated for this PUBLISH.
* @param[out] pPacketIdentifierHigh Where the high byte of the packet identifier
* is written.
* @param[in, out] pBuffer User provide buffer where the PUBLISH packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return #IOT_MQTT_SUCCESS or #IOT_MQTT_BAD_PARAMETER.
*
* @note pBuffer must be allocated by caller.
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_SerializePublish() should be used to serialize
* // MQTT Publish packet and send it to broker.
* // Example uses static memory, but dynamically allocated memory can be used as well.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendUnsubscribePacket( int xMQTTSocket )
* {
* IotMqttError_t xResult;
* IotMqttPublishInfo_t xMQTTPublishInfo;
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
* size_t xSentBytes = 0;
* uint16_t usPacketIdentifier;
* uint8_t * pusPacketIdentifierHigh;
*
* // Initialize Publish parameters. Details are out of scope for this example.
* // It will involve setting QOS, topic filter, topic filter length, payload
* // payload length.
* _initializePublish( &xMQTTPublishInfo );
*
* // Find out length of Publish packet size.
* xResult = IotMqtt_GetPublishPacketSize( &xMQTTPublishInfo, &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // Make sure the packet size is less than static buffer size
* IotMqtt_Assert( xPacketSize < mqttexampleSHARED_BUFFER_SIZE );
*
* xResult = IotMqtt_SerializePublish( &xMQTTPublishInfo,
* xRemainingLength,
* &usPacketIdentifier,
* &pusPacketIdentifierHigh,
* ucSharedBuffer,
* xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentBytes = send( xMQTTSocket, ( void * ) ucSharedBuffer, xPacketSize, 0 );
* IotMqtt_Assert( xSentBytes == xPacketSize );
* }
* @endcode
*/
/* @[declare_mqtt_serializepublish] */
IotMqttError_t IotMqtt_SerializePublish( IotMqttPublishInfo_t * pPublishInfo,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t ** pPacketIdentifierHigh,
uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializepublish] */
/**
* @brief Generate a DISCONNECT packet
*
* @param[in, out] pBuffer User provide buffer where the DISCONNECT packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return returns #IOT_MQTT_SUCCESS or #IOT_MQTT_BAD_PARAMETER
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example below shows how IotMqtt_SerializeDisconnect() should be used.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendDisconnectRequest( int xMQTTSocket )
* {
* size_t xSentBytes = 0;
*
* // Disconnect is fixed length packet, therefore there is no need to calculate the size,
* // just makes sure static buffer can accommodate disconnect request.
* IotMqtt_Assert( MQTT_PACKET_DISCONNECT_SIZE <= mqttexampleSHARED_BUFFER_SIZE );
*
* // Serialize Disconnect packet into static buffer (dynamically allocated buffer can be used as well)
* xResult = IotMqtt_SerializeDisconnect( ucSharedBuffer, MQTT_PACKET_DISCONNECT_SIZE );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentByte = send( xMQTTSocket, ( void * ) ucSharedBuffer, MQTT_PACKET_DISCONNECT_SIZE, 0 );
* IotMqtt_Assert( xSentByte == MQTT_PACKET_DISCONNECT_SIZE );
* }
*
* @endcode
*/
/* @[declare_mqtt_serializedisconnect] */
IotMqttError_t IotMqtt_SerializeDisconnect( uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializedisconnect] */
/**
* @brief Generate a PINGREQ packet.
*
* @param[in, out] pBuffer User provide buffer where the PINGREQ packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return #IOT_MQTT_SUCCESS or #IOT_MQTT_BAD_PARAMETER.
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example below shows how IotMqtt_SerializePingReq() should be used.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendPingRequest( int xMQTTSocket )
* {
* size_t xSentBytes = 0;
*
* // PingReq is fixed length packet, therefore there is no need to calculate the size,
* // just makes sure static buffer can accommodate Ping request.
* IotMqtt_Assert( MQTT_PACKET_PINGREQ_SIZE <= mqttexampleSHARED_BUFFER_SIZE );
*
* xResult = IotMqtt_SerializePingreq( ucSharedBuffer, MQTT_PACKET_PINGREQ_SIZE );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentByte = send( xMQTTSocket, ( void * ) ucSharedBuffer, MQTT_PACKET_DISCONNECT_SIZE, 0 );
* IotMqtt_Assert( xSentByte == MQTT_PACKET_PINGREQ_SIZE);
* }
* @endcode
*/
/* @[declare_mqtt_serializepingreq] */
IotMqttError_t IotMqtt_SerializePingreq( uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializepingreq] */
/**
* @brief Extract MQTT packet type and length from incoming packet
*
* @param[in, out] pIncomingPacket Pointer to IotMqttPacketInfo_t structure
* where type, remaining length and packet identifier are stored.
* @param[in] getNextByte Pointer to platform specific function which is used
* to extract type and length from incoming received stream (see example ).
* @param[in] pNetworkConnection Pointer to platform specific network connection
* which is used by getNextByte to receive network data
*
* @return #IOT_MQTT_SUCCESS on successful extraction of type and length,
* #IOT_MQTT_BAD_RESPONSE on failure.
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how to implement getNetxByte function with posix sockets.
* // Note: IotMqttGetNextByte_t typedef IotMqttError_t (* IotMqttGetNextByte_t)( void * pNetworkContext,
* // uint8_t * pNextByte );
* // Note: It is assumed that socket is already created and connected,
*
* IotMqttError_t getNextByte( void * pContext,
* uint8_t * pNextByte )
* {
* int socket = ( int ) ( *pvContext );
* int receivedBytes;
* IotMqttError_t result;
*
* receivedBytes = recv( socket, ( void * ) pNextByte, sizeof( uint8_t ), 0 );
*
* if( receivedBytes == sizeof( uint8_t ) )
* {
* result = IOT_MQTT_SUCCESS;
* }
* else
* {
* result = IOT_MQTT_TIMEOUT;
* }
*
* return result;
* }
*
* // Example below shows how IotMqtt_GetIncomingMQTTPacketTypeAndLength() is used to extract type
* // and length from incoming ping response.
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* void getTypeAndLengthFromIncomingMQTTPingResponse( int xMQTTSocket )
* {
* IotMqttPacketInfo_t xIncomingPacket;
* IotMqttError_t xResult = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &xIncomingPacket, getNextByte, ( void * ) xMQTTSocket );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* IotMqtt_Assert( xIncomingPacket.type == MQTT_PACKET_TYPE_PINGRESP );
* }
* @endcode
*
*/
/* @[declare_mqtt_getincomingmqttpackettypeandlength] */
IotMqttError_t IotMqtt_GetIncomingMQTTPacketTypeAndLength( IotMqttPacketInfo_t * pIncomingPacket,
IotMqttGetNextByte_t getNextByte,
void * pNetworkConnection );
/* @[declare_mqtt_getincomingmqttpackettypeandlength] */
/**
* @brief Deserialize incoming publish packet.
*
* @param[in, out] pMqttPacket The caller of this API sets type, remainingLength and pRemainingData.
* On success, packetIdentifier and pubInfo will be set by the function.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_SERVER_REFUSED
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example below shows how IotMqtt_DeserializePublish() used to extract contents of incoming
* // Publish. xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* void processIncomingPublish( int xMQTTSocket )
* {
* IotMqttError_t xResult;
* IotMqttPacketInfo_t xIncomingPacket;
*
* xResult = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &xIncomingPacket, getNextByte, ( void * ) xMQTTSocket );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* IotMqtt_Assert( ( xIncomingPacket.type & 0xf0 ) == MQTT_PACKET_TYPE_PUBLISH );
* IotMqtt_Assert( xIncomingPacket.remainingLength <= mqttexampleSHARED_BUFFER_SIZE );
*
* // Receive the remaining bytes.
* if( recv( xMQTTSocket, ( void * ) ucSharedBuffer, xIncomingPacket.remainingLength, 0 ) == xIncomingPacket.remainingLength )
* {
* xIncomingPacket.pRemainingData = ucSharedBuffer;
*
* if( IotMqtt_DeserializePublish( &xIncomingPacket ) != IOT_MQTT_SUCCESS )
* {
* xResult = IOT_MQTT_BAD_RESPONSE;
* }
* else
* {
* // Process incoming Publish.
* IotLogInfo( "Incoming QOS : %d\n", xIncomingPacket.pubInfo.qos );
* IotLogInfo( "Incoming Publish Topic Name: %.*s\n", xIncomingPacket.pubInfo.topicNameLength, xIncomingPacket.pubInfo.pTopicName );
* IotLogInfo( "Incoming Publish Message : %.*s\n", xIncomingPacket.pubInfo.payloadLength, xIncomingPacket.pubInfo.pPayload );
* }
* }
* else
* {
* xResult = IOT_MQTT_NETWORK_ERROR;
* }
*
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* }
* @endcode
*/
/* @[declare_mqtt_deserializepublish] */
IotMqttError_t IotMqtt_DeserializePublish( IotMqttPacketInfo_t * pMqttPacket );
/* @[declare_mqtt_deserializepublish] */
/**
* @brief Deserialize incoming ack packets.
*
* @param[in, out] pMqttPacket The caller of this API sets type, remainingLength and pRemainingData.
* On success, packetIdentifier will be set.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_SERVER_REFUSED
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example below shows how IotMqtt_DeserializeResponse() is used to process unsubscribe ack.
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* void processUnsubscribeAck( int xMQTTSocket )
* {
* IotMqttError_t xResult;
* IotMqttPacketInfo_t xIncomingPacket;
*
* xResult = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &xIncomingPacket, getNextByte, ( void * ) xMQTTSocket );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* IotMqtt_Assert( xIncomingPacket.type == MQTT_PACKET_TYPE_UNSUBACK );
* IotMqtt_Assert( xIncomingPacket.remainingLength <= sizeof( ucSharedBuffer ) );
*
* // Receive the remaining bytes.
* if( recv( xMQTTSocket, ( void * ) ucSharedBuffer, xIncomingPacket.remainingLength, 0 ) == xIncomingPacket.remainingLength )
* {
* xIncomingPacket.pRemainingData = ucSharedBuffer;
*
* if( IotMqtt_DeserializeResponse( &xIncomingPacket ) != IOT_MQTT_SUCCESS )
* {
* xResult = IOT_MQTT_BAD_RESPONSE;
* }
* }
* else
* {
* xResult = IOT_MQTT_NETWORK_ERROR;
* }
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* }
* @endcode
*/
/* @[declare_mqtt_deserializeresponse] */
IotMqttError_t IotMqtt_DeserializeResponse( IotMqttPacketInfo_t * pMqttPacket );
/* @[declare_mqtt_deserializeresponse] */
#endif /* ifndef IOT_MQTT_SERIALIZE_H_ */

View file

@ -0,0 +1,884 @@
/*
* IoT MQTT V2.1.0
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_mqtt_network.c
* @brief Implements functions involving transport layer connections.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* Error handling include. */
#include "iot_error.h"
/* MQTT internal include. */
#include "private/iot_mqtt_internal.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
/* Atomics include. */
#include "iot_atomic.h"
/*-----------------------------------------------------------*/
/**
* @brief Check if an incoming packet type is valid.
*
* @param[in] packetType The packet type to check.
*
* @return `true` if the packet type is valid; `false` otherwise.
*/
static bool _incomingPacketValid( uint8_t packetType );
/**
* @brief Get an incoming MQTT packet from the network.
*
* @param[in] pNetworkConnection Network connection to use for receive, which
* may be different from the network connection associated with the MQTT connection.
* @param[in] pMqttConnection The associated MQTT connection.
* @param[out] pIncomingPacket Output parameter for the incoming packet.
*
* @return #IOT_MQTT_SUCCESS, #IOT_MQTT_NO_MEMORY or #IOT_MQTT_BAD_RESPONSE.
*/
static IotMqttError_t _getIncomingPacket( void * pNetworkConnection,
const _mqttConnection_t * pMqttConnection,
_mqttPacket_t * pIncomingPacket );
/**
* @brief Deserialize a packet received from the network.
*
* @param[in] pMqttConnection The associated MQTT connection.
* @param[in] pIncomingPacket The packet received from the network.
*
* @return #IOT_MQTT_SUCCESS, #IOT_MQTT_NO_MEMORY, #IOT_MQTT_NETWORK_ERROR,
* #IOT_MQTT_SCHEDULING_ERROR, #IOT_MQTT_BAD_RESPONSE, or #IOT_MQTT_SERVER_REFUSED.
*/
static IotMqttError_t _deserializeIncomingPacket( _mqttConnection_t * pMqttConnection,
_mqttPacket_t * pIncomingPacket );
/**
* @brief Send a PUBACK for a received QoS 1 PUBLISH packet.
*
* @param[in] pMqttConnection Which connection the PUBACK should be sent over.
* @param[in] packetIdentifier Which packet identifier to include in PUBACK.
*/
static void _sendPuback( _mqttConnection_t * pMqttConnection,
uint16_t packetIdentifier );
/**
* @brief Flush a packet from the stream of incoming data.
*
* This function is called when memory for a packet cannot be allocated. The
* packet is flushed from the stream of incoming data so that the next packet
* may be read.
*
* @param[in] pNetworkConnection Network connection to use for receive, which
* may be different from the network connection associated with the MQTT connection.
* @param[in] pMqttConnection The associated MQTT connection.
* @param[in] length The length of the packet to flush.
*/
static void _flushPacket( void * pNetworkConnection,
const _mqttConnection_t * pMqttConnection,
size_t length );
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Declaration of local MQTT serializer override selectors
*/
#if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttGetPacketType_t,
_getPacketTypeFunc,
_IotMqtt_GetPacketType,
getPacketType )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttGetRemainingLength_t,
_getRemainingLengthFunc,
_IotMqtt_GetRemainingLength,
getRemainingLength )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getConnackDeserializer,
_IotMqtt_DeserializeConnack,
deserialize.connack )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getPublishDeserializer,
_IotMqtt_DeserializePublish,
deserialize.publish )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getPubackDeserializer,
_IotMqtt_DeserializePuback,
deserialize.puback )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getSubackDeserializer,
_IotMqtt_DeserializeSuback,
deserialize.suback )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getUnsubackDeserializer,
_IotMqtt_DeserializeUnsuback,
deserialize.unsuback )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getPingrespDeserializer,
_IotMqtt_DeserializePingresp,
deserialize.pingresp )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttSerializePuback_t,
_getMqttPubackSerializer,
_IotMqtt_SerializePuback,
serialize.puback )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttFreePacket_t,
_getMqttFreePacketFunc,
_IotMqtt_FreePacket,
freePacket )
#else /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */
#define _getPacketTypeFunc( pSerializer ) _IotMqtt_GetPacketType
#define _getRemainingLengthFunc( pSerializer ) _IotMqtt_GetRemainingLength
#define _getConnackDeserializer( pSerializer ) _IotMqtt_DeserializeConnack
#define _getPublishDeserializer( pSerializer ) _IotMqtt_DeserializePublish
#define _getPubackDeserializer( pSerializer ) _IotMqtt_DeserializePuback
#define _getSubackDeserializer( pSerializer ) _IotMqtt_DeserializeSuback
#define _getUnsubackDeserializer( pSerializer ) _IotMqtt_DeserializeUnsuback
#define _getPingrespDeserializer( pSerializer ) _IotMqtt_DeserializePingresp
#define _getMqttPubackSerializer( pSerializer ) _IotMqtt_SerializePuback
#define _getMqttFreePacketFunc( pSerializer ) _IotMqtt_FreePacket
#endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */
/** @endcond */
/*-----------------------------------------------------------*/
static bool _incomingPacketValid( uint8_t packetType )
{
bool status = true;
/* Check packet type. Mask out lower bits to ignore flags. */
switch( packetType & 0xf0 )
{
/* Valid incoming packet types. */
case MQTT_PACKET_TYPE_CONNACK:
case MQTT_PACKET_TYPE_PUBLISH:
case MQTT_PACKET_TYPE_PUBACK:
case MQTT_PACKET_TYPE_SUBACK:
case MQTT_PACKET_TYPE_UNSUBACK:
case MQTT_PACKET_TYPE_PINGRESP:
break;
/* Any other packet type is invalid. */
default:
status = false;
break;
}
return status;
}
/*-----------------------------------------------------------*/
static IotMqttError_t _getIncomingPacket( void * pNetworkConnection,
const _mqttConnection_t * pMqttConnection,
_mqttPacket_t * pIncomingPacket )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
size_t dataBytesRead = 0;
/* No buffer for remaining data should be allocated. */
IotMqtt_Assert( pIncomingPacket->pRemainingData == NULL );
IotMqtt_Assert( pIncomingPacket->remainingLength == 0 );
/* Read the packet type, which is the first byte available. */
pIncomingPacket->type = _getPacketTypeFunc( pMqttConnection->pSerializer )( pNetworkConnection,
pMqttConnection->pNetworkInterface );
/* Check that the incoming packet type is valid. */
if( _incomingPacketValid( pIncomingPacket->type ) == false )
{
IotLogError( "(MQTT connection %p) Unknown packet type %02x received.",
pMqttConnection,
pIncomingPacket->type );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Read the remaining length. */
pIncomingPacket->remainingLength = _getRemainingLengthFunc( pMqttConnection->pSerializer )( pNetworkConnection,
pMqttConnection->pNetworkInterface );
if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Allocate a buffer for the remaining data and read the data. */
if( pIncomingPacket->remainingLength > 0 )
{
pIncomingPacket->pRemainingData = IotMqtt_MallocMessage( pIncomingPacket->remainingLength );
if( pIncomingPacket->pRemainingData == NULL )
{
IotLogError( "(MQTT connection %p) Failed to allocate buffer of length "
"%lu for incoming packet type %lu.",
pMqttConnection,
( unsigned long ) pIncomingPacket->remainingLength,
( unsigned long ) pIncomingPacket->type );
_flushPacket( pNetworkConnection, pMqttConnection, pIncomingPacket->remainingLength );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );
}
else
{
EMPTY_ELSE_MARKER;
}
dataBytesRead = pMqttConnection->pNetworkInterface->receive( pNetworkConnection,
pIncomingPacket->pRemainingData,
pIncomingPacket->remainingLength );
if( dataBytesRead != pIncomingPacket->remainingLength )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Clean up on error. */
IOT_FUNCTION_CLEANUP_BEGIN();
if( status != IOT_MQTT_SUCCESS )
{
if( pIncomingPacket->pRemainingData != NULL )
{
IotMqtt_FreeMessage( pIncomingPacket->pRemainingData );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/
static IotMqttError_t _deserializeIncomingPacket( _mqttConnection_t * pMqttConnection,
_mqttPacket_t * pIncomingPacket )
{
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
_mqttOperation_t * pOperation = NULL;
/* A buffer for remaining data must be allocated if remaining length is not 0. */
IotMqtt_Assert( ( pIncomingPacket->remainingLength > 0 ) ==
( pIncomingPacket->pRemainingData != NULL ) );
/* Only valid packets should be given to this function. */
IotMqtt_Assert( _incomingPacketValid( pIncomingPacket->type ) == true );
/* Mask out the low bits of packet type to ignore flags. */
switch( ( pIncomingPacket->type & 0xf0 ) )
{
case MQTT_PACKET_TYPE_CONNACK:
IotLogDebug( "(MQTT connection %p) CONNACK in data stream.", pMqttConnection );
/* Deserialize CONNACK and notify of result. */
status = _getConnackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
pOperation = _IotMqtt_FindOperation( pMqttConnection,
IOT_MQTT_CONNECT,
NULL );
if( pOperation != NULL )
{
pOperation->u.operation.status = status;
_IotMqtt_Notify( pOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
break;
case MQTT_PACKET_TYPE_PUBLISH:
IotLogDebug( "(MQTT connection %p) PUBLISH in data stream.", pMqttConnection );
/* Allocate memory to handle the incoming PUBLISH. */
pOperation = IotMqtt_MallocOperation( sizeof( _mqttOperation_t ) );
if( pOperation == NULL )
{
IotLogWarn( "Failed to allocate memory for incoming PUBLISH." );
status = IOT_MQTT_NO_MEMORY;
break;
}
else
{
/* Set the members of the incoming PUBLISH operation. */
( void ) memset( pOperation, 0x00, sizeof( _mqttOperation_t ) );
pOperation->incomingPublish = true;
pOperation->pMqttConnection = pMqttConnection;
pIncomingPacket->u.pIncomingPublish = pOperation;
}
/* Deserialize incoming PUBLISH. */
status = _getPublishDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
if( status == IOT_MQTT_SUCCESS )
{
/* Send a PUBACK for QoS 1 PUBLISH. */
if( pOperation->u.publish.publishInfo.qos == IOT_MQTT_QOS_1 )
{
_sendPuback( pMqttConnection, pIncomingPacket->packetIdentifier );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Transfer ownership of the received MQTT packet to the PUBLISH operation. */
pOperation->u.publish.pReceivedData = pIncomingPacket->pRemainingData;
pIncomingPacket->pRemainingData = NULL;
/* Add the PUBLISH to the list of operations pending processing. */
IotMutex_Lock( &( pMqttConnection->referencesMutex ) );
IotListDouble_InsertHead( &( pMqttConnection->pendingProcessing ),
&( pOperation->link ) );
IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );
/* Increment the MQTT connection reference count before scheduling an
* incoming PUBLISH. */
if( _IotMqtt_IncrementConnectionReferences( pMqttConnection ) == true )
{
/* Schedule PUBLISH for callback invocation. */
status = _IotMqtt_ScheduleOperation( pOperation, _IotMqtt_ProcessIncomingPublish, 0 );
}
else
{
status = IOT_MQTT_NETWORK_ERROR;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Free PUBLISH operation on error. */
if( status != IOT_MQTT_SUCCESS )
{
/* Check ownership of the received MQTT packet. */
if( pOperation->u.publish.pReceivedData != NULL )
{
/* Retrieve the pointer MQTT packet pointer so it may be freed later. */
IotMqtt_Assert( pIncomingPacket->pRemainingData == NULL );
pIncomingPacket->pRemainingData = ( uint8_t * ) pOperation->u.publish.pReceivedData;
}
else
{
EMPTY_ELSE_MARKER;
}
/* Remove operation from pending processing list. */
IotMutex_Lock( &( pMqttConnection->referencesMutex ) );
if( IotLink_IsLinked( &( pOperation->link ) ) == true )
{
IotListDouble_Remove( &( pOperation->link ) );
}
else
{
EMPTY_ELSE_MARKER;
}
IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );
IotMqtt_Assert( pOperation != NULL );
IotMqtt_FreeOperation( pOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
break;
case MQTT_PACKET_TYPE_PUBACK:
IotLogDebug( "(MQTT connection %p) PUBACK in data stream.", pMqttConnection );
/* Deserialize PUBACK and notify of result. */
status = _getPubackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
pOperation = _IotMqtt_FindOperation( pMqttConnection,
IOT_MQTT_PUBLISH_TO_SERVER,
&( pIncomingPacket->packetIdentifier ) );
if( pOperation != NULL )
{
pOperation->u.operation.status = status;
_IotMqtt_Notify( pOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
break;
case MQTT_PACKET_TYPE_SUBACK:
IotLogDebug( "(MQTT connection %p) SUBACK in data stream.", pMqttConnection );
/* Deserialize SUBACK and notify of result. */
pIncomingPacket->u.pMqttConnection = pMqttConnection;
status = _getSubackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
pOperation = _IotMqtt_FindOperation( pMqttConnection,
IOT_MQTT_SUBSCRIBE,
&( pIncomingPacket->packetIdentifier ) );
if( pOperation != NULL )
{
pOperation->u.operation.status = status;
_IotMqtt_Notify( pOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
break;
case MQTT_PACKET_TYPE_UNSUBACK:
IotLogDebug( "(MQTT connection %p) UNSUBACK in data stream.", pMqttConnection );
/* Deserialize UNSUBACK and notify of result. */
status = _getUnsubackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
pOperation = _IotMqtt_FindOperation( pMqttConnection,
IOT_MQTT_UNSUBSCRIBE,
&( pIncomingPacket->packetIdentifier ) );
if( pOperation != NULL )
{
pOperation->u.operation.status = status;
_IotMqtt_Notify( pOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
break;
default:
/* The only remaining valid type is PINGRESP. */
IotMqtt_Assert( ( pIncomingPacket->type & 0xf0 ) == MQTT_PACKET_TYPE_PINGRESP );
IotLogDebug( "(MQTT connection %p) PINGRESP in data stream.", pMqttConnection );
/* Deserialize PINGRESP. */
status = _getPingrespDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
if( status == IOT_MQTT_SUCCESS )
{
if( Atomic_CompareAndSwap_u32( &( pMqttConnection->pingreq.u.operation.periodic.ping.failure ),
0,
1 ) == 1 )
{
IotLogDebug( "(MQTT connection %p) PINGRESP successfully parsed.",
pMqttConnection );
}
else
{
IotLogWarn( "(MQTT connection %p) Unexpected PINGRESP received.",
pMqttConnection );
}
}
else
{
EMPTY_ELSE_MARKER;
}
break;
}
if( status != IOT_MQTT_SUCCESS )
{
IotLogError( "(MQTT connection %p) Packet parser status %s.",
pMqttConnection,
IotMqtt_strerror( status ) );
}
else
{
EMPTY_ELSE_MARKER;
}
return status;
}
/*-----------------------------------------------------------*/
static void _sendPuback( _mqttConnection_t * pMqttConnection,
uint16_t packetIdentifier )
{
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
_mqttOperation_t * pPubackOperation = NULL;
IotLogDebug( "(MQTT connection %p) Sending PUBACK for received PUBLISH %hu.",
pMqttConnection,
packetIdentifier );
/* Create a PUBACK operation. */
status = _IotMqtt_CreateOperation( pMqttConnection,
0,
NULL,
&pPubackOperation );
if( status != IOT_MQTT_SUCCESS )
{
IOT_GOTO_CLEANUP();
}
/* Set the operation type. */
pPubackOperation->u.operation.type = IOT_MQTT_PUBACK;
/* Generate a PUBACK packet from the packet identifier. */
status = _getMqttPubackSerializer( pMqttConnection->pSerializer )( packetIdentifier,
&( pPubackOperation->u.operation.pMqttPacket ),
&( pPubackOperation->u.operation.packetSize ) );
if( status != IOT_MQTT_SUCCESS )
{
IOT_GOTO_CLEANUP();
}
/* Add the PUBACK operation to the send queue for network transmission. */
status = _IotMqtt_ScheduleOperation( pPubackOperation,
_IotMqtt_ProcessSend,
0 );
if( status != IOT_MQTT_SUCCESS )
{
IotLogError( "(MQTT connection %p) Failed to enqueue PUBACK for sending.",
pMqttConnection );
IOT_GOTO_CLEANUP();
}
else
{
EMPTY_ELSE_MARKER;
}
/* Clean up on error. */
IOT_FUNCTION_CLEANUP_BEGIN();
if( status != IOT_MQTT_SUCCESS )
{
if( pPubackOperation != NULL )
{
_IotMqtt_DestroyOperation( pPubackOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
/*-----------------------------------------------------------*/
static void _flushPacket( void * pNetworkConnection,
const _mqttConnection_t * pMqttConnection,
size_t length )
{
size_t bytesFlushed = 0;
uint8_t receivedByte = 0;
for( bytesFlushed = 0; bytesFlushed < length; bytesFlushed++ )
{
( void ) _IotMqtt_GetNextByte( pNetworkConnection,
pMqttConnection->pNetworkInterface,
&receivedByte );
}
}
/*-----------------------------------------------------------*/
bool _IotMqtt_GetNextByte( void * pNetworkConnection,
const IotNetworkInterface_t * pNetworkInterface,
uint8_t * pIncomingByte )
{
bool status = false;
uint8_t incomingByte = 0;
size_t bytesReceived = 0;
/* Attempt to read 1 byte. */
bytesReceived = pNetworkInterface->receive( pNetworkConnection,
&incomingByte,
1 );
/* Set the output parameter and return success if 1 byte was read. */
if( bytesReceived == 1 )
{
*pIncomingByte = incomingByte;
status = true;
}
else
{
/* Network receive must return 0 on failure. */
IotMqtt_Assert( bytesReceived == 0 );
}
return status;
}
/*-----------------------------------------------------------*/
void _IotMqtt_CloseNetworkConnection( IotMqttDisconnectReason_t disconnectReason,
_mqttConnection_t * pMqttConnection )
{
IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;
IotNetworkError_t closeStatus = IOT_NETWORK_SUCCESS;
IotMqttCallbackParam_t callbackParam = { .u.message = { 0 } };
void * pNetworkConnection = NULL, * pDisconnectCallbackContext = NULL;
/* Disconnect callback function. */
void ( * disconnectCallback )( void *,
IotMqttCallbackParam_t * ) = NULL;
/* Network close function. */
IotNetworkError_t ( * closeConnection) ( IotNetworkConnection_t ) = NULL;
/* Mark the MQTT connection as disconnected and the keep-alive as failed. */
IotMutex_Lock( &( pMqttConnection->referencesMutex ) );
pMqttConnection->disconnected = true;
if( pMqttConnection->pingreq.u.operation.periodic.ping.keepAliveMs != 0 )
{
/* Keep-alive must have a PINGREQ allocated. */
IotMqtt_Assert( pMqttConnection->pingreq.u.operation.pMqttPacket != NULL );
IotMqtt_Assert( pMqttConnection->pingreq.u.operation.packetSize != 0 );
/* PINGREQ provides a reference to the connection, so reference count must
* be nonzero. */
IotMqtt_Assert( pMqttConnection->references > 0 );
/* Attempt to cancel the keep-alive job. */
taskPoolStatus = IotTaskPool_TryCancel( IOT_SYSTEM_TASKPOOL,
pMqttConnection->pingreq.job,
NULL );
/* Clean up keep-alive if its job was successfully canceled. Otherwise,
* the executing keep-alive job will clean up itself. */
if( taskPoolStatus == IOT_TASKPOOL_SUCCESS )
{
/* Free the packet */
_getMqttFreePacketFunc( pMqttConnection->pSerializer )( pMqttConnection->pingreq.u.operation.pMqttPacket );
/* Clear data about the keep-alive. */
pMqttConnection->pingreq.u.operation.periodic.ping.keepAliveMs = 0;
pMqttConnection->pingreq.u.operation.pMqttPacket = NULL;
pMqttConnection->pingreq.u.operation.packetSize = 0;
/* Keep-alive is cleaned up; decrement reference count. Since this
* function must be followed with a call to DISCONNECT, a check to
* destroy the connection is not done here. */
pMqttConnection->references--;
IotLogDebug( "(MQTT connection %p) Keep-alive job canceled and cleaned up.",
pMqttConnection );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Copy the function pointers and contexts, as the MQTT connection may be
* modified after the mutex is released. */
disconnectCallback = pMqttConnection->disconnectCallback.function;
pDisconnectCallbackContext = pMqttConnection->disconnectCallback.pCallbackContext;
closeConnection = pMqttConnection->pNetworkInterface->close;
pNetworkConnection = pMqttConnection->pNetworkConnection;
IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );
/* Close the network connection. */
if( closeConnection != NULL )
{
closeStatus = closeConnection( pNetworkConnection );
if( closeStatus == IOT_NETWORK_SUCCESS )
{
IotLogInfo( "(MQTT connection %p) Network connection closed.", pMqttConnection );
}
else
{
IotLogWarn( "(MQTT connection %p) Failed to close network connection, error %d.",
pMqttConnection,
closeStatus );
}
}
else
{
IotLogWarn( "(MQTT connection %p) No network close function was set. Network connection"
" not closed.", pMqttConnection );
}
/* Invoke the disconnect callback. */
if( disconnectCallback != NULL )
{
/* Set the members of the callback parameter. */
callbackParam.mqttConnection = pMqttConnection;
callbackParam.u.disconnectReason = disconnectReason;
disconnectCallback( pDisconnectCallbackContext,
&callbackParam );
}
else
{
EMPTY_ELSE_MARKER;
}
}
/*-----------------------------------------------------------*/
void IotMqtt_ReceiveCallback( IotNetworkConnection_t pNetworkConnection,
void * pReceiveContext )
{
IotMqttError_t status = IOT_MQTT_SUCCESS;
_mqttPacket_t incomingPacket = { .u.pMqttConnection = NULL };
/* Cast context to correct type. */
_mqttConnection_t * pMqttConnection = ( _mqttConnection_t * ) pReceiveContext;
/* Read an MQTT packet from the network. */
status = _getIncomingPacket( pNetworkConnection,
pMqttConnection,
&incomingPacket );
if( status == IOT_MQTT_SUCCESS )
{
/* Deserialize the received packet. */
status = _deserializeIncomingPacket( pMqttConnection,
&incomingPacket );
/* Free any buffers allocated for the MQTT packet. */
if( incomingPacket.pRemainingData != NULL )
{
IotMqtt_FreeMessage( incomingPacket.pRemainingData );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Close the network connection on a bad response. */
if( status == IOT_MQTT_BAD_RESPONSE )
{
IotLogError( "(MQTT connection %p) Error processing incoming data. Closing connection.",
pMqttConnection );
_IotMqtt_CloseNetworkConnection( IOT_MQTT_BAD_PACKET_RECEIVED,
pMqttConnection );
}
else
{
EMPTY_ELSE_MARKER;
}
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_GetIncomingMQTTPacketTypeAndLength( IotMqttPacketInfo_t * pIncomingPacket,
IotMqttGetNextByte_t getNextByte,
void * pNetworkConnection )
{
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Read the packet type, which is the first byte available. */
if( getNextByte( pNetworkConnection, &( pIncomingPacket->type ) ) == IOT_MQTT_SUCCESS )
{
/* Check that the incoming packet type is valid. */
if( _incomingPacketValid( pIncomingPacket->type ) == false )
{
IotLogError( "(MQTT connection) Unknown packet type %02x received.",
pIncomingPacket->type );
status = IOT_MQTT_BAD_RESPONSE;
}
else
{
/* Read the remaining length. */
pIncomingPacket->remainingLength = _IotMqtt_GetRemainingLength_Generic( pNetworkConnection,
getNextByte );
if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID )
{
status = IOT_MQTT_BAD_RESPONSE;
}
}
}
else
{
status = IOT_MQTT_NETWORK_ERROR;
}
return status;
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,645 @@
/*
* IoT MQTT V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_mqtt_subscription.c
* @brief Implements functions that manage subscriptions for an MQTT connection.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <string.h>
/* Error handling include. */
#include "iot_error.h"
/* MQTT internal include. */
#include "private/iot_mqtt_internal.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
/*-----------------------------------------------------------*/
/**
* @brief First parameter to #_topicMatch.
*/
typedef struct _topicMatchParams
{
const char * pTopicName; /**< @brief The topic name to parse. */
uint16_t topicNameLength; /**< @brief Length of #_topicMatchParams_t.pTopicName. */
bool exactMatchOnly; /**< @brief Whether to allow wildcards or require exact matches. */
} _topicMatchParams_t;
/**
* @brief First parameter to #_packetMatch.
*/
typedef struct _packetMatchParams
{
uint16_t packetIdentifier; /**< Packet identifier to match. */
int32_t order; /**< Order to match. Set to #MQTT_REMOVE_ALL_SUBSCRIPTIONS to ignore. */
} _packetMatchParams_t;
/*-----------------------------------------------------------*/
/**
* @brief Matches a topic name (from a publish) with a topic filter (from a
* subscription).
*
* @param[in] pSubscriptionLink Pointer to the link member of an #_mqttSubscription_t.
* @param[in] pMatch Pointer to a #_topicMatchParams_t.
*
* @return `true` if the arguments match the subscription topic filter; `false`
* otherwise.
*/
static bool _topicMatch( const IotLink_t * pSubscriptionLink,
void * pMatch );
/**
* @brief Matches a packet identifier and order.
*
* @param[in] pSubscriptionLink Pointer to the link member of an #_mqttSubscription_t.
* @param[in] pMatch Pointer to a #_packetMatchParams_t.
*
* @return `true` if the arguments match the subscription's packet info; `false`
* otherwise.
*/
static bool _packetMatch( const IotLink_t * pSubscriptionLink,
void * pMatch );
/*-----------------------------------------------------------*/
static bool _topicMatch( const IotLink_t * pSubscriptionLink,
void * pMatch )
{
IOT_FUNCTION_ENTRY( bool, false );
uint16_t nameIndex = 0, filterIndex = 0;
/* Because this function is called from a container function, the given link
* must never be NULL. */
IotMqtt_Assert( pSubscriptionLink != NULL );
_mqttSubscription_t * pSubscription = IotLink_Container( _mqttSubscription_t,
pSubscriptionLink,
link );
_topicMatchParams_t * pParam = ( _topicMatchParams_t * ) pMatch;
/* Extract the relevant strings and lengths from parameters. */
const char * pTopicName = pParam->pTopicName;
const char * pTopicFilter = pSubscription->pTopicFilter;
const uint16_t topicNameLength = pParam->topicNameLength;
const uint16_t topicFilterLength = pSubscription->topicFilterLength;
/* Check for an exact match. */
if( topicNameLength == topicFilterLength )
{
status = ( strncmp( pTopicName, pTopicFilter, topicNameLength ) == 0 );
IOT_GOTO_CLEANUP();
}
else
{
EMPTY_ELSE_MARKER;
}
/* If the topic lengths are different but an exact match is required, return
* false. */
if( pParam->exactMatchOnly == true )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
while( ( nameIndex < topicNameLength ) && ( filterIndex < topicFilterLength ) )
{
/* Check if the character in the topic name matches the corresponding
* character in the topic filter string. */
if( pTopicName[ nameIndex ] == pTopicFilter[ filterIndex ] )
{
/* Handle special corner cases as documented by the MQTT protocol spec. */
/* Filter "sport/#" also matches "sport" since # includes the parent level. */
if( nameIndex == topicNameLength - 1 )
{
if( filterIndex == topicFilterLength - 3 )
{
if( pTopicFilter[ filterIndex + 1 ] == '/' )
{
if( pTopicFilter[ filterIndex + 2 ] == '#' )
{
IOT_SET_AND_GOTO_CLEANUP( true );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Filter "sport/+" also matches the "sport/" but not "sport". */
if( nameIndex == topicNameLength - 1 )
{
if( filterIndex == topicFilterLength - 2 )
{
if( pTopicFilter[ filterIndex + 1 ] == '+' )
{
IOT_SET_AND_GOTO_CLEANUP( true );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
/* Check for wildcards. */
if( pTopicFilter[ filterIndex ] == '+' )
{
/* Move topic name index to the end of the current level.
* This is identified by '/'. */
while( nameIndex < topicNameLength && pTopicName[ nameIndex ] != '/' )
{
nameIndex++;
}
/* Increment filter index to skip '/'. */
filterIndex++;
continue;
}
else if( pTopicFilter[ filterIndex ] == '#' )
{
/* Subsequent characters don't need to be checked if the for the
* multi-level wildcard. */
IOT_SET_AND_GOTO_CLEANUP( true );
}
else
{
/* Any character mismatch other than '+' or '#' means the topic
* name does not match the topic filter. */
IOT_SET_AND_GOTO_CLEANUP( false );
}
}
/* Increment indexes. */
nameIndex++;
filterIndex++;
}
/* If the end of both strings has been reached, they match. */
if( ( nameIndex == topicNameLength ) && ( filterIndex == topicFilterLength ) )
{
IOT_SET_AND_GOTO_CLEANUP( true );
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
static bool _packetMatch( const IotLink_t * pSubscriptionLink,
void * pMatch )
{
bool match = false;
/* Because this function is called from a container function, the given link
* must never be NULL. */
IotMqtt_Assert( pSubscriptionLink != NULL );
_mqttSubscription_t * pSubscription = IotLink_Container( _mqttSubscription_t,
pSubscriptionLink,
link );
_packetMatchParams_t * pParam = ( _packetMatchParams_t * ) pMatch;
/* Compare packet identifiers. */
if( pParam->packetIdentifier == pSubscription->packetInfo.identifier )
{
/* Compare orders if order is not MQTT_REMOVE_ALL_SUBSCRIPTIONS. */
if( pParam->order == MQTT_REMOVE_ALL_SUBSCRIPTIONS )
{
match = true;
}
else
{
match = ( ( size_t ) pParam->order ) == pSubscription->packetInfo.order;
}
}
/* If this subscription should be removed, check the reference count. */
if( match == true )
{
/* Reference count must not be negative. */
IotMqtt_Assert( pSubscription->references >= 0 );
/* If the reference count is positive, this subscription cannot be
* removed yet because there are subscription callbacks using it. */
if( pSubscription->references > 0 )
{
match = false;
/* Set the unsubscribed flag. The last active subscription callback
* will remove and clean up this subscription. */
pSubscription->unsubscribed = true;
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
return match;
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_AddSubscriptions( _mqttConnection_t * pMqttConnection,
uint16_t subscribePacketIdentifier,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount )
{
IotMqttError_t status = IOT_MQTT_SUCCESS;
size_t i = 0;
_mqttSubscription_t * pNewSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
_topicMatchParams_t topicMatchParams = { .exactMatchOnly = true };
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
for( i = 0; i < subscriptionCount; i++ )
{
/* Check if this topic filter is already registered. */
topicMatchParams.pTopicName = pSubscriptionList[ i ].pTopicFilter;
topicMatchParams.topicNameLength = pSubscriptionList[ i ].topicFilterLength;
pSubscriptionLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),
NULL,
_topicMatch,
&topicMatchParams );
if( pSubscriptionLink != NULL )
{
pNewSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );
/* The lengths of exactly matching topic filters must match. */
IotMqtt_Assert( pNewSubscription->topicFilterLength == pSubscriptionList[ i ].topicFilterLength );
/* Replace the callback and packet info with the new parameters. */
pNewSubscription->callback = pSubscriptionList[ i ].callback;
pNewSubscription->packetInfo.identifier = subscribePacketIdentifier;
pNewSubscription->packetInfo.order = i;
}
else
{
/* Allocate memory for a new subscription. */
pNewSubscription = IotMqtt_MallocSubscription( sizeof( _mqttSubscription_t ) +
pSubscriptionList[ i ].topicFilterLength );
if( pNewSubscription == NULL )
{
status = IOT_MQTT_NO_MEMORY;
break;
}
else
{
/* Clear the new subscription. */
( void ) memset( pNewSubscription,
0x00,
sizeof( _mqttSubscription_t ) + pSubscriptionList[ i ].topicFilterLength );
/* Set the members of the new subscription and add it to the list. */
pNewSubscription->packetInfo.identifier = subscribePacketIdentifier;
pNewSubscription->packetInfo.order = i;
pNewSubscription->callback = pSubscriptionList[ i ].callback;
pNewSubscription->topicFilterLength = pSubscriptionList[ i ].topicFilterLength;
( void ) memcpy( pNewSubscription->pTopicFilter,
pSubscriptionList[ i ].pTopicFilter,
( size_t ) ( pSubscriptionList[ i ].topicFilterLength ) );
IotListDouble_InsertHead( &( pMqttConnection->subscriptionList ),
&( pNewSubscription->link ) );
}
}
}
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
/* If memory allocation failed, remove all previously added subscriptions. */
if( status != IOT_MQTT_SUCCESS )
{
_IotMqtt_RemoveSubscriptionByTopicFilter( pMqttConnection,
pSubscriptionList,
i );
}
else
{
EMPTY_ELSE_MARKER;
}
return status;
}
/*-----------------------------------------------------------*/
void _IotMqtt_InvokeSubscriptionCallback( _mqttConnection_t * pMqttConnection,
IotMqttCallbackParam_t * pCallbackParam )
{
_mqttSubscription_t * pSubscription = NULL;
IotLink_t * pCurrentLink = NULL, * pNextLink = NULL;
void * pCallbackContext = NULL;
void ( * callbackFunction )( void *,
IotMqttCallbackParam_t * ) = NULL;
_topicMatchParams_t topicMatchParams = { 0 };
/* Set the members of the search parameter. */
topicMatchParams.pTopicName = pCallbackParam->u.message.info.pTopicName;
topicMatchParams.topicNameLength = pCallbackParam->u.message.info.topicNameLength;
topicMatchParams.exactMatchOnly = false;
/* Prevent any other thread from modifying the subscription list while this
* function is searching. */
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
/* Search the subscription list for all matching subscriptions starting at
* the list head. */
while( true )
{
pCurrentLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),
pCurrentLink,
_topicMatch,
&topicMatchParams );
/* No subscription found. Exit loop. */
if( pCurrentLink == NULL )
{
break;
}
else
{
EMPTY_ELSE_MARKER;
}
/* Subscription found. Calculate pointer to subscription object. */
pSubscription = IotLink_Container( _mqttSubscription_t, pCurrentLink, link );
/* Subscription validation should not have allowed a NULL callback function. */
IotMqtt_Assert( pSubscription->callback.function != NULL );
/* Increment the subscription's reference count. */
( pSubscription->references )++;
/* Copy the necessary members of the subscription before releasing the
* subscription list mutex. */
pCallbackContext = pSubscription->callback.pCallbackContext;
callbackFunction = pSubscription->callback.function;
/* Unlock the subscription list mutex. */
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
/* Set the members of the callback parameter. */
pCallbackParam->mqttConnection = pMqttConnection;
pCallbackParam->u.message.pTopicFilter = pSubscription->pTopicFilter;
pCallbackParam->u.message.topicFilterLength = pSubscription->topicFilterLength;
/* Invoke the subscription callback. */
callbackFunction( pCallbackContext, pCallbackParam );
/* Lock the subscription list mutex to decrement the reference count. */
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
/* Decrement the reference count. It must still be positive. */
( pSubscription->references )--;
IotMqtt_Assert( pSubscription->references >= 0 );
/* Save the pointer to the next link in case this subscription is freed. */
pNextLink = pCurrentLink->pNext;
/* Remove this subscription if it has no references and the unsubscribed
* flag is set. */
if( pSubscription->unsubscribed == true )
{
/* An unsubscribed subscription should have been removed from the list. */
IotMqtt_Assert( IotLink_IsLinked( &( pSubscription->link ) ) == false );
/* Free subscriptions with no references. */
if( pSubscription->references == 0 )
{
IotMqtt_FreeSubscription( pSubscription );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Move current link pointer. */
pCurrentLink = pNextLink;
}
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
_IotMqtt_DecrementConnectionReferences( pMqttConnection );
}
/*-----------------------------------------------------------*/
void _IotMqtt_RemoveSubscriptionByPacket( _mqttConnection_t * pMqttConnection,
uint16_t packetIdentifier,
int32_t order )
{
_packetMatchParams_t packetMatchParams = { 0 };
/* Set the members of the search parameter. */
packetMatchParams.packetIdentifier = packetIdentifier;
packetMatchParams.order = order;
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
IotListDouble_RemoveAllMatches( &( pMqttConnection->subscriptionList ),
_packetMatch,
( void * ) ( &packetMatchParams ),
IotMqtt_FreeSubscription,
offsetof( _mqttSubscription_t, link ) );
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
}
/*-----------------------------------------------------------*/
void _IotMqtt_RemoveSubscriptionByTopicFilter( _mqttConnection_t * pMqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount )
{
size_t i = 0;
_mqttSubscription_t * pSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
_topicMatchParams_t topicMatchParams = { 0 };
/* Prevent any other thread from modifying the subscription list while this
* function is running. */
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
/* Find and remove each topic filter from the list. */
for( i = 0; i < subscriptionCount; i++ )
{
topicMatchParams.pTopicName = pSubscriptionList[ i ].pTopicFilter;
topicMatchParams.topicNameLength = pSubscriptionList[ i ].topicFilterLength;
topicMatchParams.exactMatchOnly = true;
pSubscriptionLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),
NULL,
_topicMatch,
&topicMatchParams );
if( pSubscriptionLink != NULL )
{
pSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );
/* Reference count must not be negative. */
IotMqtt_Assert( pSubscription->references >= 0 );
/* Remove subscription from list. */
IotListDouble_Remove( pSubscriptionLink );
/* Check the reference count. This subscription cannot be removed if
* there are subscription callbacks using it. */
if( pSubscription->references > 0 )
{
/* Set the unsubscribed flag. The last active subscription callback
* will remove and clean up this subscription. */
pSubscription->unsubscribed = true;
}
else
{
/* Free a subscription with no references. */
IotMqtt_FreeSubscription( pSubscription );
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
}
/*-----------------------------------------------------------*/
bool IotMqtt_IsSubscribed( IotMqttConnection_t mqttConnection,
const char * pTopicFilter,
uint16_t topicFilterLength,
IotMqttSubscription_t * const pCurrentSubscription )
{
bool status = false;
_mqttSubscription_t * pSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
_topicMatchParams_t topicMatchParams = { 0 };
/* Set the members of the search parameter. */
topicMatchParams.pTopicName = pTopicFilter;
topicMatchParams.topicNameLength = topicFilterLength;
topicMatchParams.exactMatchOnly = true;
/* Prevent any other thread from modifying the subscription list while this
* function is running. */
IotMutex_Lock( &( mqttConnection->subscriptionMutex ) );
/* Search for a matching subscription. */
pSubscriptionLink = IotListDouble_FindFirstMatch( &( mqttConnection->subscriptionList ),
NULL,
_topicMatch,
&topicMatchParams );
/* Check if a matching subscription was found. */
if( pSubscriptionLink != NULL )
{
pSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );
/* Copy the matching subscription to the output parameter. */
if( pCurrentSubscription != NULL )
{
pCurrentSubscription->pTopicFilter = pTopicFilter;
pCurrentSubscription->topicFilterLength = topicFilterLength;
pCurrentSubscription->qos = IOT_MQTT_QOS_0;
pCurrentSubscription->callback = pSubscription->callback;
}
else
{
EMPTY_ELSE_MARKER;
}
status = true;
}
else
{
EMPTY_ELSE_MARKER;
}
IotMutex_Unlock( &( mqttConnection->subscriptionMutex ) );
return status;
}
/*-----------------------------------------------------------*/
/* Provide access to internal functions and variables if testing. */
#if IOT_BUILD_TESTS == 1
#include "iot_test_access_mqtt_subscription.c"
#endif

View file

@ -0,0 +1,637 @@
/*
* IoT MQTT V2.1.0
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file iot_mqtt_validate.c
* @brief Implements functions that validate the structs of the MQTT library.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Error handling include. */
#include "iot_error.h"
/* MQTT internal include. */
#include "private/iot_mqtt_internal.h"
/**
* @brief Check that an #IotMqttPublishInfo_t is valid.
*
* @param[in] awsIotMqttMode Specifies if this PUBLISH packet is being sent to
* an AWS IoT MQTT server.
* @param[in] maximumPayloadLength Maximum payload length.
* @param[in] pPublishTypeDescription String describing the publish type.
* @param[in] pPublishInfo The #IotMqttPublishInfo_t to validate.
*
* @return `true` if `pPublishInfo` is valid; `false` otherwise.
*/
static bool _validatePublish( bool awsIotMqttMode,
size_t maximumPayloadLength,
const char * pPublishTypeDescription,
const IotMqttPublishInfo_t * pPublishInfo );
/*-----------------------------------------------------------*/
bool _IotMqtt_ValidateConnect( const IotMqttConnectInfo_t * pConnectInfo )
{
IOT_FUNCTION_ENTRY( bool, true );
uint16_t maxClientIdLength = MQTT_SERVER_MAX_CLIENTID_LENGTH;
bool enforceMaxClientIdLength = false;
/* Check for NULL. */
if( pConnectInfo == NULL )
{
IotLogError( "MQTT connection information cannot be NULL." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check that a client identifier was set. */
if( pConnectInfo->pClientIdentifier == NULL )
{
IotLogError( "Client identifier must be set." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check for a zero-length client identifier. Zero-length client identifiers
* are not allowed with clean sessions. */
if( pConnectInfo->clientIdentifierLength == 0 )
{
IotLogWarn( "A zero-length client identifier was provided." );
if( pConnectInfo->cleanSession == true )
{
IotLogError( "A zero-length client identifier cannot be used with a clean session." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check that the number of persistent session subscriptions is valid. */
if( pConnectInfo->pPreviousSubscriptions != NULL )
{
if( _IotMqtt_ValidateSubscriptionList( IOT_MQTT_SUBSCRIBE,
pConnectInfo->awsIotMqttMode,
pConnectInfo->pPreviousSubscriptions,
pConnectInfo->previousSubscriptionCount ) == false )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* If will info is provided, check that it is valid. */
if( pConnectInfo->pWillInfo != NULL )
{
if( _IotMqtt_ValidateLwtPublish( pConnectInfo->awsIotMqttMode,
pConnectInfo->pWillInfo ) == false )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* The AWS IoT MQTT service enforces a client ID length limit. */
if( pConnectInfo->awsIotMqttMode == true )
{
maxClientIdLength = AWS_IOT_MQTT_SERVER_MAX_CLIENTID_LENGTH;
enforceMaxClientIdLength = true;
}
if( pConnectInfo->clientIdentifierLength > maxClientIdLength )
{
if( enforceMaxClientIdLength == false )
{
IotLogWarn( "A client identifier length of %hu is longer than %hu, "
"which is "
"the longest client identifier a server must accept.",
pConnectInfo->clientIdentifierLength,
maxClientIdLength );
}
else
{
IotLogError( "A client identifier length of %hu exceeds the "
"maximum supported length of %hu.",
pConnectInfo->clientIdentifierLength,
maxClientIdLength );
IOT_SET_AND_GOTO_CLEANUP( false );
}
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
static bool _validatePublish( bool awsIotMqttMode,
size_t maximumPayloadLength,
const char * pPublishTypeDescription,
const IotMqttPublishInfo_t * pPublishInfo )
{
IOT_FUNCTION_ENTRY( bool, true );
/* Check for NULL. */
if( pPublishInfo == NULL )
{
IotLogError( "Publish information cannot be NULL." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check topic name for NULL or zero-length. */
if( pPublishInfo->pTopicName == NULL )
{
IotLogError( "Publish topic name must be set." );
}
else
{
EMPTY_ELSE_MARKER;
}
if( pPublishInfo->topicNameLength == 0 )
{
IotLogError( "Publish topic name length cannot be 0." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
if( pPublishInfo->payloadLength != 0 )
{
if( pPublishInfo->payloadLength > maximumPayloadLength )
{
IotLogError( "%s payload size of %zu exceeds maximum length of %zu.",
pPublishTypeDescription,
pPublishInfo->payloadLength,
maximumPayloadLength );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
if( pPublishInfo->pPayload == NULL )
{
IotLogError( "Nonzero payload length cannot have a NULL payload." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check for a valid QoS. */
if( pPublishInfo->qos != IOT_MQTT_QOS_0 )
{
if( pPublishInfo->qos != IOT_MQTT_QOS_1 )
{
IotLogError( "Publish QoS must be either 0 or 1." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check the retry parameters. */
if( pPublishInfo->retryLimit > 0 )
{
if( pPublishInfo->retryMs == 0 )
{
IotLogError( "Publish retry time must be positive." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check for compatibility with AWS IoT MQTT server. */
if( awsIotMqttMode == true )
{
/* Check for retained message. */
if( pPublishInfo->retain == true )
{
IotLogError( "AWS IoT does not support retained publish messages." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check topic name length. */
if( pPublishInfo->topicNameLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )
{
IotLogError( "AWS IoT does not support topic names longer than %d bytes.",
AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
bool _IotMqtt_ValidatePublish( bool awsIotMqttMode,
const IotMqttPublishInfo_t * pPublishInfo )
{
size_t maximumPayloadLength = MQTT_SERVER_MAX_PUBLISH_PAYLOAD_LENGTH;
if( awsIotMqttMode == true )
{
maximumPayloadLength = AWS_IOT_MQTT_SERVER_MAX_PUBLISH_PAYLOAD_LENGTH;
}
else
{
EMPTY_ELSE_MARKER;
}
return _validatePublish( awsIotMqttMode,
maximumPayloadLength,
"Publish",
pPublishInfo );
}
/*-----------------------------------------------------------*/
bool _IotMqtt_ValidateLwtPublish( bool awsIotMqttMode,
const IotMqttPublishInfo_t * pLwtPublishInfo )
{
return _validatePublish( awsIotMqttMode,
MQTT_SERVER_MAX_LWT_PAYLOAD_LENGTH,
"LWT",
pLwtPublishInfo );
}
/*-----------------------------------------------------------*/
bool _IotMqtt_ValidateOperation( IotMqttOperation_t operation )
{
IOT_FUNCTION_ENTRY( bool, true );
/* Check for NULL. */
if( operation == NULL )
{
IotLogError( "Operation reference cannot be NULL." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check that reference is waitable. */
if( ( operation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE ) != IOT_MQTT_FLAG_WAITABLE )
{
IotLogError( "Operation is not waitable." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
bool _IotMqtt_ValidateSubscriptionList( IotMqttOperationType_t operation,
bool awsIotMqttMode,
const IotMqttSubscription_t * pListStart,
size_t listSize )
{
IOT_FUNCTION_ENTRY( bool, true );
size_t i = 0;
uint16_t j = 0;
const IotMqttSubscription_t * pListElement = NULL;
/* Operation must be either subscribe or unsubscribe. */
IotMqtt_Assert( ( operation == IOT_MQTT_SUBSCRIBE ) ||
( operation == IOT_MQTT_UNSUBSCRIBE ) );
/* Check for empty list. */
if( pListStart == NULL )
{
IotLogError( "Subscription list pointer cannot be NULL." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
if( listSize == 0 )
{
IotLogError( "Empty subscription list." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* AWS IoT supports at most 8 topic filters in a single SUBSCRIBE packet. */
if( awsIotMqttMode == true )
{
if( listSize > AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE )
{
IotLogError( "AWS IoT does not support more than %d topic filters per "
"subscription request.",
AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
for( i = 0; i < listSize; i++ )
{
pListElement = &( pListStart[ i ] );
/* Check for a valid QoS and callback function when subscribing. */
if( operation == IOT_MQTT_SUBSCRIBE )
{
if( pListElement->qos != IOT_MQTT_QOS_0 )
{
if( pListElement->qos != IOT_MQTT_QOS_1 )
{
IotLogError( "Subscription QoS must be either 0 or 1." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
if( pListElement->callback.function == NULL )
{
IotLogError( "Callback function must be set." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check subscription topic filter. */
if( pListElement->pTopicFilter == NULL )
{
IotLogError( "Subscription topic filter cannot be NULL." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
if( pListElement->topicFilterLength == 0 )
{
IotLogError( "Subscription topic filter length cannot be 0." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check for compatibility with AWS IoT MQTT server. */
if( awsIotMqttMode == true )
{
/* Check topic filter length. */
if( pListElement->topicFilterLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )
{
IotLogError( "AWS IoT does not support topic filters longer than %d bytes.",
AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check that the wildcards '+' and '#' are being used correctly. */
for( j = 0; j < pListElement->topicFilterLength; j++ )
{
switch( pListElement->pTopicFilter[ j ] )
{
/* Check that the single level wildcard '+' is used correctly. */
case '+':
/* Unless '+' is the first character in the filter, it must be preceded by '/'. */
if( j > 0 )
{
if( pListElement->pTopicFilter[ j - 1 ] != '/' )
{
IotLogError( "Invalid topic filter %.*s -- '+' must be preceded by '/'.",
pListElement->topicFilterLength,
pListElement->pTopicFilter );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Unless '+' is the last character in the filter, it must be succeeded by '/'. */
if( j < pListElement->topicFilterLength - 1 )
{
if( pListElement->pTopicFilter[ j + 1 ] != '/' )
{
IotLogError( "Invalid topic filter %.*s -- '+' must be succeeded by '/'.",
pListElement->topicFilterLength,
pListElement->pTopicFilter );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
break;
/* Check that the multi-level wildcard '#' is used correctly. */
case '#':
/* '#' must be the last character in the filter. */
if( j != pListElement->topicFilterLength - 1 )
{
IotLogError( "Invalid topic filter %.*s -- '#' must be the last character.",
pListElement->topicFilterLength,
pListElement->pTopicFilter );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Unless '#' is standalone, it must be preceded by '/'. */
if( pListElement->topicFilterLength > 1 )
{
if( pListElement->pTopicFilter[ j - 1 ] != '/' )
{
IotLogError( "Invalid topic filter %.*s -- '#' must be preceded by '/'.",
pListElement->topicFilterLength,
pListElement->pTopicFilter );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
break;
default:
break;
}
}
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,8 @@
Base directory for the FreeRTOS IoT Libraries.
+ abstractions
Contains FreeRTOS specific implementations of abstractions used within the IoT
libraries.
+ c_sdk
Contains the implementations of the IoT libraries - more will be rolled out soon.

View file

@ -0,0 +1,46 @@
*** INTRODUCTION ***
This distribution currently contains demos from the FreeRTOS task pool library,
MQTT library, HTTPS Client library, Shadow library, and Jobs library.
The pre-configured projects use the FreeRTOS kernel Windows port (often
called the Windows simulator) to enable their evaluation using the free Visual
Studio tools and without needing specific microcontroller hardware.
NOTE: At this time the projects ARE A WORK IN PROGRESS and will be released in
the main FreeRTOS kernel download following full review and completion of the
documentation.
*** INSTRUCTIONS ***
Instructions for configuring and using the FreeRTOS IoT libraries are in the
following links:
+ https://www.FreeRTOS.org/task-pool/
+ https://www.FreeRTOS.org/mqtt/
+ https://www.freertos.org/https/
+ https://www.FreeRTOS.org/shadow/
+ https://www.FreeRTOS.org/jobs/
*** LOCATING THE EXAMPLE PROJECTS ***
The Visual Studio projects for each of the FreeRTOS IoT library examples are
located in sub-directories of the following top-level directories:
+ /FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/utilities
+ /FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/mqtt
+ /FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/https
+ /FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/shadow
+ /FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/jobs
*** ADDITIONAL INFORMATION ***
See http://www.freertos.org/a00017.html for full details of the FreeRTOS
directory structure
See also -
http://www.freertos.org/FreeRTOS-quick-start-guide.html
http://www.freertos.org/FAQHelp.html

View file

@ -0,0 +1,347 @@
/*
* FreeRTOS+CLI V1.0.3 (C) 2014 Real Time Engineers ltd. All rights reserved.
*
* This file is part of the FreeRTOS+CLI distribution. The FreeRTOS+CLI license
* terms are different to the FreeRTOS license terms.
*
* FreeRTOS+CLI uses a dual license model that allows the software to be used
* under a standard GPL open source license, or a commercial license. The
* standard GPL license (unlike the modified GPL license under which FreeRTOS
* itself is distributed) requires that all software statically linked with
* FreeRTOS+CLI is also distributed under the same GPL V2 license terms.
* Details of both license options follow:
*
* - Open source licensing -
* FreeRTOS+CLI is a free download and may be used, modified, evaluated and
* distributed without charge provided the user adheres to version two of the
* GNU General Public License (GPL) and does not remove the copyright notice or
* this text. The GPL V2 text is available on the gnu.org web site, and on the
* following URL: http://www.FreeRTOS.org/gpl-2.0.txt.
*
* - Commercial licensing -
* Businesses and individuals that for commercial or other reasons cannot comply
* with the terms of the GPL V2 license must obtain a low cost commercial
* license before incorporating FreeRTOS+CLI into proprietary software for
* distribution in any form. Commercial licenses can be purchased from
* http://shop.freertos.org/cli and do not require any source files to be
* changed.
*
* FreeRTOS+CLI is distributed in the hope that it will be useful. You cannot
* use FreeRTOS+CLI unless you agree that you use the software 'as is'.
* FreeRTOS+CLI is provided WITHOUT ANY WARRANTY; without even the implied
* warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they
* implied, expressed, or statutory.
*
* 1 tab == 4 spaces!
*
* http://www.FreeRTOS.org
* http://www.FreeRTOS.org/FreeRTOS-Plus
*
*/
/* Standard includes. */
#include <string.h>
#include <stdint.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
/* Utils includes. */
#include "FreeRTOS_CLI.h"
typedef struct xCOMMAND_INPUT_LIST
{
const CLI_Command_Definition_t *pxCommandLineDefinition;
struct xCOMMAND_INPUT_LIST *pxNext;
} CLI_Definition_List_Item_t;
/*
* The callback function that is executed when "help" is entered. This is the
* only default command that is always present.
*/
static BaseType_t prvHelpCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString );
/*
* Return the number of parameters that follow the command name.
*/
static int8_t prvGetNumberOfParameters( const char *pcCommandString );
/* The definition of the "help" command. This command is always at the front
of the list of registered commands. */
static const CLI_Command_Definition_t xHelpCommand =
{
"help",
"\r\nhelp:\r\n Lists all the registered commands\r\n\r\n",
prvHelpCommand,
0
};
/* The definition of the list of commands. Commands that are registered are
added to this list. */
static CLI_Definition_List_Item_t xRegisteredCommands =
{
&xHelpCommand, /* The first command in the list is always the help command, defined in this file. */
NULL /* The next pointer is initialised to NULL, as there are no other registered commands yet. */
};
/* A buffer into which command outputs can be written is declared here, rather
than in the command console implementation, to allow multiple command consoles
to share the same buffer. For example, an application may allow access to the
command interpreter by UART and by Ethernet. Sharing a buffer is done purely
to save RAM. Note, however, that the command console itself is not re-entrant,
so only one command interpreter interface can be used at any one time. For that
reason, no attempt at providing mutual exclusion to the cOutputBuffer array is
attempted. */
static char cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
/*-----------------------------------------------------------*/
BaseType_t FreeRTOS_CLIRegisterCommand( const CLI_Command_Definition_t * const pxCommandToRegister )
{
static CLI_Definition_List_Item_t *pxLastCommandInList = &xRegisteredCommands;
CLI_Definition_List_Item_t *pxNewListItem;
BaseType_t xReturn = pdFAIL;
/* Check the parameter is not NULL. */
configASSERT( pxCommandToRegister );
/* Create a new list item that will reference the command being registered. */
pxNewListItem = ( CLI_Definition_List_Item_t * ) pvPortMalloc( sizeof( CLI_Definition_List_Item_t ) );
configASSERT( pxNewListItem );
if( pxNewListItem != NULL )
{
taskENTER_CRITICAL();
{
/* Reference the command being registered from the newly created
list item. */
pxNewListItem->pxCommandLineDefinition = pxCommandToRegister;
/* The new list item will get added to the end of the list, so
pxNext has nowhere to point. */
pxNewListItem->pxNext = NULL;
/* Add the newly created list item to the end of the already existing
list. */
pxLastCommandInList->pxNext = pxNewListItem;
/* Set the end of list marker to the new list item. */
pxLastCommandInList = pxNewListItem;
}
taskEXIT_CRITICAL();
xReturn = pdPASS;
}
return xReturn;
}
/*-----------------------------------------------------------*/
BaseType_t FreeRTOS_CLIProcessCommand( const char * const pcCommandInput, char * pcWriteBuffer, size_t xWriteBufferLen )
{
static const CLI_Definition_List_Item_t *pxCommand = NULL;
BaseType_t xReturn = pdTRUE;
const char *pcRegisteredCommandString;
size_t xCommandStringLength;
/* Note: This function is not re-entrant. It must not be called from more
thank one task. */
if( pxCommand == NULL )
{
/* Search for the command string in the list of registered commands. */
for( pxCommand = &xRegisteredCommands; pxCommand != NULL; pxCommand = pxCommand->pxNext )
{
pcRegisteredCommandString = pxCommand->pxCommandLineDefinition->pcCommand;
xCommandStringLength = strlen( pcRegisteredCommandString );
/* To ensure the string lengths match exactly, so as not to pick up
a sub-string of a longer command, check the byte after the expected
end of the string is either the end of the string or a space before
a parameter. */
if( ( pcCommandInput[ xCommandStringLength ] == ' ' ) || ( pcCommandInput[ xCommandStringLength ] == 0x00 ) )
{
if( strncmp( pcCommandInput, pcRegisteredCommandString, xCommandStringLength ) == 0 )
{
/* The command has been found. Check it has the expected
number of parameters. If cExpectedNumberOfParameters is -1,
then there could be a variable number of parameters and no
check is made. */
if( pxCommand->pxCommandLineDefinition->cExpectedNumberOfParameters >= 0 )
{
if( prvGetNumberOfParameters( pcCommandInput ) != pxCommand->pxCommandLineDefinition->cExpectedNumberOfParameters )
{
xReturn = pdFALSE;
}
}
break;
}
}
}
}
if( ( pxCommand != NULL ) && ( xReturn == pdFALSE ) )
{
/* The command was found, but the number of parameters with the command
was incorrect. */
strncpy( pcWriteBuffer, "Incorrect command parameter(s). Enter \"help\" to view a list of available commands.\r\n\r\n", xWriteBufferLen );
pxCommand = NULL;
}
else if( pxCommand != NULL )
{
/* Call the callback function that is registered to this command. */
xReturn = pxCommand->pxCommandLineDefinition->pxCommandInterpreter( pcWriteBuffer, xWriteBufferLen, pcCommandInput );
/* If xReturn is pdFALSE, then no further strings will be returned
after this one, and pxCommand can be reset to NULL ready to search
for the next entered command. */
if( xReturn == pdFALSE )
{
pxCommand = NULL;
}
}
else
{
/* pxCommand was NULL, the command was not found. */
strncpy( pcWriteBuffer, "Command not recognised. Enter 'help' to view a list of available commands.\r\n\r\n", xWriteBufferLen );
xReturn = pdFALSE;
}
return xReturn;
}
/*-----------------------------------------------------------*/
char *FreeRTOS_CLIGetOutputBuffer( void )
{
return cOutputBuffer;
}
/*-----------------------------------------------------------*/
const char *FreeRTOS_CLIGetParameter( const char *pcCommandString, UBaseType_t uxWantedParameter, BaseType_t *pxParameterStringLength )
{
UBaseType_t uxParametersFound = 0;
const char *pcReturn = NULL;
*pxParameterStringLength = 0;
while( uxParametersFound < uxWantedParameter )
{
/* Index the character pointer past the current word. If this is the start
of the command string then the first word is the command itself. */
while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) != ' ' ) )
{
pcCommandString++;
}
/* Find the start of the next string. */
while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) == ' ' ) )
{
pcCommandString++;
}
/* Was a string found? */
if( *pcCommandString != 0x00 )
{
/* Is this the start of the required parameter? */
uxParametersFound++;
if( uxParametersFound == uxWantedParameter )
{
/* How long is the parameter? */
pcReturn = pcCommandString;
while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) != ' ' ) )
{
( *pxParameterStringLength )++;
pcCommandString++;
}
if( *pxParameterStringLength == 0 )
{
pcReturn = NULL;
}
break;
}
}
else
{
break;
}
}
return pcReturn;
}
/*-----------------------------------------------------------*/
static BaseType_t prvHelpCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
{
static const CLI_Definition_List_Item_t * pxCommand = NULL;
BaseType_t xReturn;
( void ) pcCommandString;
if( pxCommand == NULL )
{
/* Reset the pxCommand pointer back to the start of the list. */
pxCommand = &xRegisteredCommands;
}
/* Return the next command help string, before moving the pointer on to
the next command in the list. */
strncpy( pcWriteBuffer, pxCommand->pxCommandLineDefinition->pcHelpString, xWriteBufferLen );
pxCommand = pxCommand->pxNext;
if( pxCommand == NULL )
{
/* There are no more commands in the list, so there will be no more
strings to return after this one and pdFALSE should be returned. */
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
return xReturn;
}
/*-----------------------------------------------------------*/
static int8_t prvGetNumberOfParameters( const char *pcCommandString )
{
int8_t cParameters = 0;
BaseType_t xLastCharacterWasSpace = pdFALSE;
/* Count the number of space delimited words in pcCommandString. */
while( *pcCommandString != 0x00 )
{
if( ( *pcCommandString ) == ' ' )
{
if( xLastCharacterWasSpace != pdTRUE )
{
cParameters++;
xLastCharacterWasSpace = pdTRUE;
}
}
else
{
xLastCharacterWasSpace = pdFALSE;
}
pcCommandString++;
}
/* If the command string ended with spaces, then there will have been too
many parameters counted. */
if( xLastCharacterWasSpace == pdTRUE )
{
cParameters--;
}
/* The value returned is one less than the number of space delimited words,
as the first word should be the command itself. */
return cParameters;
}

View file

@ -0,0 +1,120 @@
/*
* FreeRTOS+CLI V1.0.3 (C) 2014 Real Time Engineers ltd. All rights reserved.
*
* This file is part of the FreeRTOS+CLI distribution. The FreeRTOS+CLI license
* terms are different to the FreeRTOS license terms.
*
* FreeRTOS+CLI uses a dual license model that allows the software to be used
* under a standard GPL open source license, or a commercial license. The
* standard GPL license (unlike the modified GPL license under which FreeRTOS
* itself is distributed) requires that all software statically linked with
* FreeRTOS+CLI is also distributed under the same GPL V2 license terms.
* Details of both license options follow:
*
* - Open source licensing -
* FreeRTOS+CLI is a free download and may be used, modified, evaluated and
* distributed without charge provided the user adheres to version two of the
* GNU General Public License (GPL) and does not remove the copyright notice or
* this text. The GPL V2 text is available on the gnu.org web site, and on the
* following URL: http://www.FreeRTOS.org/gpl-2.0.txt.
*
* - Commercial licensing -
* Businesses and individuals that for commercial or other reasons cannot comply
* with the terms of the GPL V2 license must obtain a low cost commercial
* license before incorporating FreeRTOS+CLI into proprietary software for
* distribution in any form. Commercial licenses can be purchased from
* http://shop.freertos.org/cli and do not require any source files to be
* changed.
*
* FreeRTOS+CLI is distributed in the hope that it will be useful. You cannot
* use FreeRTOS+CLI unless you agree that you use the software 'as is'.
* FreeRTOS+CLI is provided WITHOUT ANY WARRANTY; without even the implied
* warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they
* implied, expressed, or statutory.
*
* 1 tab == 4 spaces!
*
* http://www.FreeRTOS.org
* http://www.FreeRTOS.org/FreeRTOS-Plus
*
*/
#ifndef COMMAND_INTERPRETER_H
#define COMMAND_INTERPRETER_H
/* The prototype to which callback functions used to process command line
commands must comply. pcWriteBuffer is a buffer into which the output from
executing the command can be written, xWriteBufferLen is the length, in bytes of
the pcWriteBuffer buffer, and pcCommandString is the entire string as input by
the user (from which parameters can be extracted).*/
typedef BaseType_t (*pdCOMMAND_LINE_CALLBACK)( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString );
/* The structure that defines command line commands. A command line command
should be defined by declaring a const structure of this type. */
typedef struct xCOMMAND_LINE_INPUT
{
const char * const pcCommand; /* The command that causes pxCommandInterpreter to be executed. For example "help". Must be all lower case. */
const char * const pcHelpString; /* String that describes how to use the command. Should start with the command itself, and end with "\r\n". For example "help: Returns a list of all the commands\r\n". */
const pdCOMMAND_LINE_CALLBACK pxCommandInterpreter; /* A pointer to the callback function that will return the output generated by the command. */
int8_t cExpectedNumberOfParameters; /* Commands expect a fixed number of parameters, which may be zero. */
} CLI_Command_Definition_t;
/* For backward compatibility. */
#define xCommandLineInput CLI_Command_Definition_t
/*
* Register the command passed in using the pxCommandToRegister parameter.
* Registering a command adds the command to the list of commands that are
* handled by the command interpreter. Once a command has been registered it
* can be executed from the command line.
*/
BaseType_t FreeRTOS_CLIRegisterCommand( const CLI_Command_Definition_t * const pxCommandToRegister );
/*
* Runs the command interpreter for the command string "pcCommandInput". Any
* output generated by running the command will be placed into pcWriteBuffer.
* xWriteBufferLen must indicate the size, in bytes, of the buffer pointed to
* by pcWriteBuffer.
*
* FreeRTOS_CLIProcessCommand should be called repeatedly until it returns pdFALSE.
*
* pcCmdIntProcessCommand is not reentrant. It must not be called from more
* than one task - or at least - by more than one task at a time.
*/
BaseType_t FreeRTOS_CLIProcessCommand( const char * const pcCommandInput, char * pcWriteBuffer, size_t xWriteBufferLen );
/*-----------------------------------------------------------*/
/*
* A buffer into which command outputs can be written is declared in the
* main command interpreter, rather than in the command console implementation,
* to allow application that provide access to the command console via multiple
* interfaces to share a buffer, and therefore save RAM. Note, however, that
* the command interpreter itself is not re-entrant, so only one command
* console interface can be used at any one time. For that reason, no attempt
* is made to provide any mutual exclusion mechanism on the output buffer.
*
* FreeRTOS_CLIGetOutputBuffer() returns the address of the output buffer.
*/
char *FreeRTOS_CLIGetOutputBuffer( void );
/*
* Return a pointer to the xParameterNumber'th word in pcCommandString.
*/
const char *FreeRTOS_CLIGetParameter( const char *pcCommandString, UBaseType_t uxWantedParameter, BaseType_t *pxParameterStringLength );
#endif /* COMMAND_INTERPRETER_H */

View file

@ -0,0 +1,27 @@
Changes between V1.0.2 and V1.0.3 released
+ Previously, and in line with good software engineering practice, the
FreeRTOS coding standard did not permit the use of char types that were
not explicitly qualified as either signed or unsigned. As a result char
pointers used to reference strings required casts, as did the use of any
standard string handling functions. The casts ensured compiler warnings
were not generated by compilers that defaulted unqualified char types to
be signed or compilers that defaulted unqualified char types to be
unsigned. As it has in later MISRA standards, this rule has now been
relaxed, and unqualified char types are now permitted, but only when:
1) The char is used to point to a human readable text string.
2) The char is used to hold a single ASCII character.
Changes between V1.0.1 and V1.0.2 released 14/10/2013
+ Changed double quotes (") to single quotes (') in the help string to
allow the strings to be used with JSON in FreeRTOS+Nabto.
Changes between V1.0.0 and V1.0.1 released 05/07/2012
+ Change the name of the structure used to map a function that implements
a CLI command to the string used to call the command from
xCommandLineInput to CLI_Command_Definition_t, as it was always intended
to be. A #define was added to map the old name to the new name for
reasons of backward compatibility.

View file

@ -0,0 +1,7 @@
Note the FreeRTOS+CLI license terms are different to the FreeRTOS license terms.
FreeRTOS+CLI is dual licensed. The files are provided here under an unmodified
open source GNU GPL license. Commercial licenses are also available, and are
provided free for some hardware platforms. See http://www.FreeRTOS.org/cli

View file

@ -0,0 +1,5 @@
[InternetShortcut]
URL=http://www.freertos.org/cli
IDList=
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,2

View file

@ -0,0 +1,4 @@
Contains source and header files that implement FreeRTOS+CLI. See
http://www.FreeRTOS.org/cli for documentation and license information.

View file

@ -0,0 +1,58 @@
Changes between 160112 and 160908 releases
NOTE: The 160908 release is a maintenance release for the 160112 single
interface labs release - not a release of the current development branch.
+ ff-deltree() now correctly handles deleted file entries.
+ Simplified mapping of standard library functions to their Visual Studio
equivalents.
+ ffconfigMIN_CLUSTERS_FAT32 and ffconfigMIN_CLUSTERS_FAT16 introduced to
allow the minimum disk sizes for the two FAT file system types to be
smaller than is permitted by Windows.
Changes between 150825 and 160111 releases
+ New device support: Demo applications and example drivers are provided
for Atmel SAM4E and ST STM32F4 microcontrollers.
+ Various updates to improve compliance with the FreeRTOS coding standard.
+ Modified the stdio tests so they can be executed on SD cards, where the
test files might already exists on power on - previously the tests were
only executed on RAM disks which are always known to be empty on power on.
+ Added ff_deltree() implementation, with note of caution about its use as
it uses recursion ( ff_deltree() recursively removes a directory and
everything contained by it).
+ Update the Zynq project to use version 2015.4 of the Xilinx SDK. This
driver dynamically recognises all types of memory cards.
+ The path cache is cleared when a disk is re-mounted, allowing a disk to be
hot swapped.
Bug fixes resulting from testing performed while converting the acquired
component to be FreeRTOS+ compliant:
+ Fix bug in FF_FindNext() when using 'ffconfigFINDAPI_ALLOW_WILDCARDS'.
+ Fix bug in ff_fat.c when using 'ffconfigFSINFO_TRUSTED' and when the
stored free cluster count equals ~0ul (which means: not filled in) as this
was interpreted as having 4294967295 free clusters.
+ FF_Open() now checks file permissions before truncating a file, previously
the file was truncated first.
+ Fix typo in source of FF_isEOF().
+ FF_ExtendFile() now only attempts to reserve new clusters if it is
actually necessary.
+ FF_Format() now correctly fills in the number of free clusters for FAT32.
+ FF_Partition() has been updated to use ffconfigMAX_PARTITIONS in all
cases, whereas previously some legacy code was assuming a fixed maximum
number of partitions.
+ FF_DeleteIOManager() now deletes the IO manager!
Changes for 150825 (?)
+ Issue fixed in FF_Move().
+ Improved handling of files and directories that start with a '.' character.
+ Changed the locking mechanisms from mutexes to an event group.
+ Add FF_ERR_DRIVER_NOMEDIUM to better handle media being removed and
re-inserted.
+ Fix re-entrancy issue in FF_CheckValid().
+ Remove hashes for deleted files.
+ General structural work.

View file

@ -0,0 +1,66 @@
> is there a repository I should be aware of when it comes to FreeRTOS Labs?
Unfortunately there isn't. FreeRTOS+FAT has become a bit of an orphan. All time and energy goes to the products in the [AWS/FreeRTOS release](https://github.com/aws/amazon-freertos)
But I am still actively maintaining FreeRTOS+FAT: implementing requests and fixes.
Please comment your findings with the above +FAT release.
Here is a complete list of all changes since the 160919 Labs release:
● `ff_dir.c : FF_MkDir()`
refuse to create a directory with an empty name
● `ff_fat.c : FF_GetFreeSize()`
in case of an error, return 0
● `ff_file.c : FF_FileSize()`
This function returns the size of a file, or a negative number in case of an error.
Hence, the maximum size returned was returned is 2 GB.
We've added a new function:
`FF_Error_t FF_GetFileSize( FF_FILE *pxFile, uint32_t *pulSize );`
which separates the length from the error code.
● `ff_file.c : FF_ExtendFile()`
Sometimes while building up a file, it may be more efficient not to flush the changes immediately. When defining `ffconfigFILE_EXTEND_FLUSHES_BUFFERS` as `0`, there is no immediate flushing.
● `ff_file.c : FF_Seek()`
Seeking didn't work for sizes larger than 2 GB. Although the parameter `int32_t lOffset` is signed, it will now be casted to 32-bits unsigned:
`ulPosition = ( uint32_t )lOffset;`
All options (`SET`, `CUR`, and `END` ) re-tested.
● `ff_file.c : FF_Close()`
`FF_FlushCache()` is now called at the end of the function in order to also write the last changes.
● `ff_format.c : FF_Format()`
A variable `lRemaining` should have been declared unsigned: `uint32_t`.
Also: a small optimisation for large SD-cards:
"Putting the FAT-table into the second 4MB erase block gives a higher performance and a longer life-time"
● `ff_format.c : FF_Partition()`
`ulHiddenSectors` must be at least `1`
( see [this post](https://sourceforge.net/p/freertos/discussion/382005/thread/d2b6524a/?limit=250#e1ea) )
● `ff_ioman.c : FF_CreateIOManger()`
`FF_CreateEvents` shall only be called in case an `pxIOManager` was successfully created.
● `ff_ioman.c : FF_DeleteIOManager()`
Added a call to `FF_DeleteEvents()`, which will delete the event group belonging to the I/O manager.
● `ff_ioman.c : FF_FlushCache()`
Added a user application hook per disk: `fnFlushApplicationHook()`. This is useful for e.g. NAND-drivers.
● `ff_ioman.c : FF_Mount()`
Make sure that `pcVolumeLabel` is null-terminated.
● `ff_ioman.c : FF_IncreaseFreeClusters()`
A lock is needed before `ulLastFreeCluster` can be changed. But before taking this lock, check if has already been taken to avoid a dead lock.
● `ff_locking.c : FF_DeleteEvents`
This is a new function which deletes ( frees up ) the event group belonging to an I/O manager.
● `ff_stdio.c : ff_filelength()`
This function will now call `FF_GetFileSize()` in order to get the `unsigned` length of a file, increasing the maximum reported size from 2 to 4 GB.
● `ff_sys.c : *`
This module combines several I/O managers ( = disks ) into a single file system that starts with a root `"/"`.
All code has been re-written ( re-styled ) and re-tested.

View file

@ -0,0 +1,5 @@
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,2
[InternetShortcut]
URL=http://www.freertos.org/fat
IDList=

View file

@ -0,0 +1,256 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
/**
* @file ff_crc.c
* @ingroup CRC
*
* @defgroup CRC CRC Checksums for Strings
* @brief Provides fast hashing functions.
*
**/
#include "ff_headers.h"
/*static*/ const uint32_t crc32_table[ 256 ] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
uint32_t FF_GetCRC32( uint8_t *pbyData, uint32_t stLength )
{
register uint32_t crc = 0xFFFFFFFF;
while( stLength-- != 0 )
{
crc = ( ( crc >> 8 ) & 0x00FFFFFF ) ^ crc32_table[ ( crc ^ *( pbyData++ ) ) & 0x000000FF ];
}
return crc ^ 0xFFFFFFFF;
}
static const uint8_t crc16_table_low[ 256 ] =
{
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x000, 0x0c1, 0x081, 0x040, 0x001, 0x0c0, 0x080, 0x041,
0x001, 0x0c0, 0x080, 0x041, 0x000, 0x0c1, 0x081, 0x040,
};
static const uint8_t crc16_table_high[256] =
{
0x000, 0x0c0, 0x0c1, 0x001, 0x0c3, 0x003, 0x002, 0x0c2,
0x0c6, 0x006, 0x007, 0x0c7, 0x005, 0x0c5, 0x0c4, 0x004,
0x0cc, 0x00c, 0x00d, 0x0cd, 0x00f, 0x0cf, 0x0ce, 0x00e,
0x00a, 0x0ca, 0x0cb, 0x00b, 0x0c9, 0x009, 0x008, 0x0c8,
0x0d8, 0x018, 0x019, 0x0d9, 0x01b, 0x0db, 0x0da, 0x01a,
0x01e, 0x0de, 0x0df, 0x01f, 0x0dd, 0x01d, 0x01c, 0x0dc,
0x014, 0x0d4, 0x0d5, 0x015, 0x0d7, 0x017, 0x016, 0x0d6,
0x0d2, 0x012, 0x013, 0x0d3, 0x011, 0x0d1, 0x0d0, 0x010,
0x0f0, 0x030, 0x031, 0x0f1, 0x033, 0x0f3, 0x0f2, 0x032,
0x036, 0x0f6, 0x0f7, 0x037, 0x0f5, 0x035, 0x034, 0x0f4,
0x03c, 0x0fc, 0x0fd, 0x03d, 0x0ff, 0x03f, 0x03e, 0x0fe,
0x0fa, 0x03a, 0x03b, 0x0fb, 0x039, 0x0f9, 0x0f8, 0x038,
0x028, 0x0e8, 0x0e9, 0x029, 0x0eb, 0x02b, 0x02a, 0x0ea,
0x0ee, 0x02e, 0x02f, 0x0ef, 0x02d, 0x0ed, 0x0ec, 0x02c,
0x0e4, 0x024, 0x025, 0x0e5, 0x027, 0x0e7, 0x0e6, 0x026,
0x022, 0x0e2, 0x0e3, 0x023, 0x0e1, 0x021, 0x020, 0x0e0,
0x0a0, 0x060, 0x061, 0x0a1, 0x063, 0x0a3, 0x0a2, 0x062,
0x066, 0x0a6, 0x0a7, 0x067, 0x0a5, 0x065, 0x064, 0x0a4,
0x06c, 0x0ac, 0x0ad, 0x06d, 0x0af, 0x06f, 0x06e, 0x0ae,
0x0aa, 0x06a, 0x06b, 0x0ab, 0x069, 0x0a9, 0x0a8, 0x068,
0x078, 0x0b8, 0x0b9, 0x079, 0x0bb, 0x07b, 0x07a, 0x0ba,
0x0be, 0x07e, 0x07f, 0x0bf, 0x07d, 0x0bd, 0x0bc, 0x07c,
0x0b4, 0x074, 0x075, 0x0b5, 0x077, 0x0b7, 0x0b6, 0x076,
0x072, 0x0b2, 0x0b3, 0x073, 0x0b1, 0x071, 0x070, 0x0b0,
0x050, 0x090, 0x091, 0x051, 0x093, 0x053, 0x052, 0x092,
0x096, 0x056, 0x057, 0x097, 0x055, 0x095, 0x094, 0x054,
0x09c, 0x05c, 0x05d, 0x09d, 0x05f, 0x09f, 0x09e, 0x05e,
0x05a, 0x09a, 0x09b, 0x05b, 0x099, 0x059, 0x058, 0x098,
0x088, 0x048, 0x049, 0x089, 0x04b, 0x08b, 0x08a, 0x04a,
0x04e, 0x08e, 0x08f, 0x04f, 0x08d, 0x04d, 0x04c, 0x08c,
0x044, 0x084, 0x085, 0x045, 0x087, 0x047, 0x046, 0x086,
0x082, 0x042, 0x043, 0x083, 0x041, 0x081, 0x080, 0x040,
};
/*****************************************************************************
* Description: Function to 16 bit CRC check a block of memory
*
* Parameters: pbyData - Pointer to the source data
* stLength - The length to CRC
*
* Return value: The 16 bit CRC value
*
*****************************************************************************/
uint16_t FF_GetCRC16( uint8_t *pbyData, uint32_t stLength )
{
uint8_t bTableValue;
uint16_t wCRC = 0;
while( stLength-- != 0 )
{
bTableValue = ( uint8_t ) ( (wCRC & 0x00FF ) ^ *pbyData++ );
wCRC = ( uint16_t ) ( ( ( crc16_table_high[ bTableValue ] ) << 8 ) +
( crc16_table_low[ bTableValue ] ^ ( ( wCRC >> 8 ) & 0x00FF ) ) );
}
return wCRC;
}
static const uint8_t crc8_table[256] =
{
0, 94, 188, 226, 97, 63, 221, 131,
194, 156, 126, 32, 163, 253, 31, 65,
157, 195, 33, 127, 252, 162, 64, 30,
95, 1, 227, 189, 62, 96, 130, 220,
35, 125, 159, 193, 66, 28, 254, 160,
225, 191, 93, 3, 128, 222, 60, 98,
190, 224, 2, 92, 223, 129, 99, 61,
124, 34, 192, 158, 29, 67, 161, 255,
70, 24, 250, 164, 39, 121, 155, 197,
132, 218, 56, 102, 229, 187, 89, 7,
219, 133, 103, 57, 186, 228, 6, 88,
25, 71, 165, 251, 120, 38, 196, 154,
101, 59, 217, 135, 4, 90, 184, 230,
167, 249, 27, 69, 198, 152, 122, 36,
248, 166, 68, 26, 153, 199, 37, 123,
58, 100, 134, 216, 91, 5, 231, 185,
140, 210, 48, 110, 237, 179, 81, 15,
78, 16, 242, 172, 47, 113, 147, 205,
17, 79, 173, 243, 112, 46, 204, 146,
211, 141, 111, 49, 178, 236, 14, 80,
175, 241, 19, 77, 206, 144, 114, 44,
109, 51, 209, 143, 12, 82, 176, 238,
50, 108, 142, 208, 83, 13, 239, 177,
240, 174, 76, 18, 145, 207, 45, 115,
202, 148, 118, 40, 171, 245, 23, 73,
8, 86, 180, 234, 105, 55, 213, 139,
87, 9, 235, 181, 54, 104, 138, 212,
149, 203, 41, 119, 244, 170, 72, 22,
233, 183, 85, 11, 136, 214, 52, 106,
43, 117, 151, 201, 74, 20, 246, 168,
116, 42, 200, 150, 21, 75, 169, 247,
182, 232, 10, 84, 215, 137, 107, 53
};
/*****************************************************************************
* Description: Function to CRC check a block of memory
*
* Parameters: pbyData - Pointer to the source data
* stLength - The length to CRC
*
* Return value: The 8 bit CRC value
*
*****************************************************************************/
uint8_t FF_GetCRC8( uint8_t *pbyData, uint32_t stLength )
{
uint8_t byCRC = 0, byData;
while( stLength-- != 0 )
{
byData = *pbyData++;
byCRC = crc8_table[ ( byCRC ^ byData ) ];
}
return byCRC;
}

View file

@ -0,0 +1,235 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
#include <stdio.h>
#include <string.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "portable.h"
#include "ff_headers.h"
#include "ff_devices.h"
#ifndef ARRAY_SIZE
# define ARRAY_SIZE( x ) ( int )( sizeof( x ) / sizeof( x )[ 0 ] )
#endif
#if( ffconfigDEV_SUPPORT == 0 )
#error No use to include this module if ffconfigDEV_SUPPORT is disabled
#endif /* ffconfigDEV_SUPPORT == 0 */
struct SFileCache
{
char pcFileName[16];
uint32_t ulFileLength;
uint32_t ulFilePointer;
};
struct SFileCache xFiles[ 16 ];
enum eCACHE_ACTION
{
eCACHE_LOOKUP,
eCACHE_ADD,
eCACHE_REMOVE,
};
const char pcDevicePath[] = ffconfigDEV_PATH;
struct SFileCache *pxFindFile( const char *pcFname, enum eCACHE_ACTION eAction )
{
BaseType_t xIndex, xFreeIndex = -1;
struct SFileCache *pxResult = NULL;
for( xIndex = 0; xIndex < ARRAY_SIZE( xFiles ); xIndex++ )
{
if( xFiles[ xIndex ].pcFileName[ 0 ] == '\0' )
{
if( xFreeIndex < 0 )
{
xFreeIndex = xIndex;
}
}
else if( strcmp( xFiles[ xIndex ].pcFileName, pcFname ) == 0 )
{
if( eAction == eCACHE_REMOVE )
{
xFiles[ xIndex ].pcFileName[ 0 ] = '\0';
}
pxResult = xFiles + xIndex;
break;
}
}
if( ( pxResult == NULL ) && ( eAction == eCACHE_ADD ) && ( xFreeIndex >= 0 ) )
{
pxResult = xFiles + xFreeIndex;
snprintf( pxResult->pcFileName, sizeof( pxResult->pcFileName ), "%s", pcFname );
pxResult->ulFileLength = 0;
pxResult->ulFilePointer = 0;
}
return pxResult;
}
BaseType_t xCheckDevicePath( const char *pcPath )
{
BaseType_t xDevLength;
BaseType_t xPathLength;
BaseType_t xIsDevice;
xDevLength = sizeof( pcDevicePath ) - 1;
xPathLength = strlen( pcPath );
/* System "/dev" should not match with "/device/etc". */
if( ( xPathLength >= xDevLength ) &&
( memcmp( pcDevicePath, pcPath, xDevLength ) == 0 ) &&
( ( pcPath[ xDevLength ] == '\0' ) || ( pcPath[ xDevLength ] == '/' ) ) )
{
xIsDevice = FF_DEV_CHAR_DEV;
}
else
{
xIsDevice = FF_DEV_NO_DEV;
}
return xIsDevice;
}
BaseType_t FF_Device_Open( const char *pcPath, FF_FILE *pxStream )
{
uint8_t ucIsDevice;
ucIsDevice = xCheckDevicePath( pcPath );
if( ucIsDevice != pdFALSE )
{
const char *pcBaseName = pcPath;
if( memcmp( pcBaseName, pcDevicePath, sizeof( pcDevicePath ) - 1 ) == 0 )
{
pcBaseName = pcBaseName + sizeof( pcDevicePath );
}
pxStream->pxDevNode = pxFindFile( pcBaseName, eCACHE_ADD );
if( pxStream->pxDevNode != NULL )
{
pxStream->pxDevNode->ulFilePointer = 0;
if( ( pxStream->ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND | FF_MODE_CREATE ) ) == 0 )
{
pxStream->ulFileSize = pxStream->pxDevNode->ulFileLength;
}
}
}
return ucIsDevice;
}
void FF_Device_Close( FF_FILE * pxStream )
{
if( pxStream->pxDevNode != NULL )
{
pxStream->ulFileSize = 0ul;
pxStream->ulFilePointer = 0ul;
}
}
size_t FF_Device_Read( void *pvBuf, size_t lSize, size_t lCount, FF_FILE * pxStream )
{
lCount *= lSize;
return lCount;
}
size_t FF_Device_Write( const void *pvBuf, size_t lSize, size_t lCount, FF_FILE * pxStream )
{
lCount *= lSize;
if( pxStream->pxDevNode != NULL )
{
pxStream->pxDevNode->ulFilePointer += lCount;
if( pxStream->pxDevNode->ulFileLength < pxStream->pxDevNode->ulFilePointer )
{
pxStream->pxDevNode->ulFileLength = pxStream->pxDevNode->ulFilePointer;
}
}
return lCount;
}
int FF_Device_Seek( FF_FILE *pxStream, long lOffset, int iWhence )
{
if( pxStream->pxDevNode != NULL )
{
if( iWhence == FF_SEEK_SET )
{
pxStream->pxDevNode->ulFilePointer = lOffset;
}
else if( iWhence == FF_SEEK_END )
{
pxStream->pxDevNode->ulFilePointer = pxStream->pxDevNode->ulFileLength - lOffset;
}
}
return 0;
}
int FF_Device_GetDirEnt( const char *pcPath, FF_DirEnt_t *pxDirEnt )
{
BaseType_t xIsDotDir = 0;
if( pxDirEnt->pcFileName[ 0 ] == '.' )
{
if( ( pxDirEnt->pcFileName[ 1 ] == '.' ) &&
( pxDirEnt->pcFileName[ 2 ] == '\0' ) )
{
xIsDotDir = 2;
}
else if( pxDirEnt->pcFileName[ 1 ] == '\0' )
{
xIsDotDir = 1;
}
}
if( xIsDotDir == 0 )
{
struct SFileCache *pxDevNode;
pxDevNode = pxFindFile( pxDirEnt->pcFileName, eCACHE_LOOKUP );
pxDirEnt->ucIsDeviceDir = FF_DEV_CHAR_DEV;
if( pxDevNode != NULL )
{
pxDirEnt->ulFileSize = pxDevNode->ulFileLength;
}
else if( pxDirEnt->ulFileSize < 2048 )
{
pxDirEnt->ulFileSize = 2048;
}
}
return 1024;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,311 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
/**
* @file ff_error.c
* @ingroup ERROR
*
* @defgroup ERR Error Message
* @brief Used to return human readable strings for FreeRTOS+FAT error codes.
*
**/
#include <stdio.h>
/* _HT_ with the new GNU settings, the prototype of [v]snprintf()
is not included in stdio.h Don't know why. */
int snprintf( char *, size_t, const char *, ... );
#include "ff_headers.h"
#if !defined( ARRAY_SIZE )
#define ARRAY_SIZE( x ) ( int )( sizeof( x )/sizeof( x )[ 0 ] )
#endif
/* This if-block spans the rest of the source file.*/
#if( ffconfigDEBUG != 0 )
const struct _FFMODULETAB
{
const char * const strModuleName;
const uint8_t ucModuleID;
} xFreeRTOSFATModuleTable[] =
{
{ "Unknown Module", 1}, /* 1 here is ok, as the GetError functions start at the end of the table. */
{ "ff_ioman.c", FF_GETMODULE( FF_MODULE_IOMAN ) },
{ "ff_dir.c", FF_GETMODULE( FF_MODULE_DIR ) },
{ "ff_file.c", FF_GETMODULE( FF_MODULE_FILE ) },
{ "ff_fat.c", FF_GETMODULE( FF_MODULE_FAT ) },
{ "ff_crc.c", FF_GETMODULE( FF_MODULE_CRC ) },
{ "ff_format.c", FF_GETMODULE( FF_MODULE_FORMAT ) },
{ "ff_memory.c", FF_GETMODULE( FF_MODULE_MEMORY ) },
{ "ff_string.c", FF_GETMODULE( FF_MODULE_STRING ) },
{ "ff_locking.c", FF_GETMODULE( FF_MODULE_LOCKING ) },
{ "ff_time.c", FF_GETMODULE( FF_MODULE_TIME ) },
{ "Platform Driver", FF_GETMODULE( FF_MODULE_DRIVER ) },
};
#if( ffconfigHAS_FUNCTION_TAB != 0 )
const struct _FFFUNCTIONTAB
{
const char * const strFunctionName;
const uint16_t ucFunctionID;
} xFreeRTOSFATFunctionTable[] =
{
{ "Unknown Function", 1},
/*----- FF_IOManager_t - The FreeRTOS+FAT I/O Manager */
{ "FF_CreateIOManger", FF_GETMOD_FUNC( FF_CREATEIOMAN ) },
{ "FF_DeleteIOManager", FF_GETMOD_FUNC( FF_DESTROYIOMAN ) },
{ "FF_Mount", FF_GETMOD_FUNC( FF_MOUNT ) },
{ "FF_Unmount", FF_GETMOD_FUNC( FF_UNMOUNT ) },
{ "FF_FlushCache", FF_GETMOD_FUNC( FF_FLUSHCACHE ) },
{ "FF_GetPartitionBlockSize", FF_GETMOD_FUNC( FF_GETPARTITIONBLOCKSIZE ) },
{ "FF_BlockRead", FF_GETMOD_FUNC( FF_BLOCKREAD ) },
{ "FF_BlockWrite", FF_GETMOD_FUNC( FF_BLOCKWRITE ) },
{ "FF_DetermineFatType", FF_GETMOD_FUNC( FF_DETERMINEFATTYPE ) },
{ "FF_GetEfiPartitionEntry", FF_GETMOD_FUNC( FF_GETEFIPARTITIONENTRY ) },
{ "FF_UserDriver", FF_GETMOD_FUNC( FF_USERDRIVER ) },
{ "FF_DecreaseFreeClusters", FF_GETMOD_FUNC( FF_DECREASEFREECLUSTERS ) },
{ "FF_IncreaseFreeClusters", FF_GETMOD_FUNC( FF_INCREASEFREECLUSTERS ) },
{ "FF_PartitionSearch", FF_GETMOD_FUNC( FF_PARTITIONSEARCH ) },
{ "FF_ParseExtended", FF_GETMOD_FUNC( FF_PARSEEXTENDED ) },
/*----- FF_DIR - The FreeRTOS+FAT directory handling routines */
{ "FF_FetchEntryWithContext", FF_GETMOD_FUNC( FF_FETCHENTRYWITHCONTEXT ) },
{ "FF_PushEntryWithContext", FF_GETMOD_FUNC( FF_PUSHENTRYWITHCONTEXT ) },
{ "FF_GetEntry", FF_GETMOD_FUNC( FF_GETENTRY ) },
{ "FF_FindFirst", FF_GETMOD_FUNC( FF_FINDFIRST ) },
{ "FF_FindNext", FF_GETMOD_FUNC( FF_FINDNEXT ) },
{ "FF_RewindFind", FF_GETMOD_FUNC( FF_REWINDFIND ) },
{ "FF_FindFreeDirent", FF_GETMOD_FUNC( FF_FINDFREEDIRENT ) },
{ "FF_PutEntry", FF_GETMOD_FUNC( FF_PUTENTRY ) },
{ "FF_CreateShortName", FF_GETMOD_FUNC( FF_CREATESHORTNAME ) },
{ "FF_CreateLFNs", FF_GETMOD_FUNC( FF_CREATELFNS ) },
{ "FF_ExtendDirectory", FF_GETMOD_FUNC( FF_EXTENDDIRECTORY ) },
{ "FF_MkDir", FF_GETMOD_FUNC( FF_MKDIR ) },
{ "FF_Traverse", FF_GETMOD_FUNC( FF_TRAVERSE ) },
{ "FF_FindDir", FF_GETMOD_FUNC( FF_FINDDIR ) },
/*----- FF_FILE - The FreeRTOS+FAT file handling routines */
{ "FF_GetModeBits", FF_GETMOD_FUNC( FF_GETMODEBITS ) },
{ "FF_Open", FF_GETMOD_FUNC( FF_OPEN ) },
{ "FF_isDirEmpty", FF_GETMOD_FUNC( FF_ISDIREMPTY ) },
{ "FF_RmDir", FF_GETMOD_FUNC( FF_RMDIR ) },
{ "FF_RmFile", FF_GETMOD_FUNC( FF_RMFILE ) },
{ "FF_Move", FF_GETMOD_FUNC( FF_MOVE ) },
{ "FF_isEOF", FF_GETMOD_FUNC( FF_ISEOF ) },
{ "FF_GetSequentialClusters", FF_GETMOD_FUNC( FF_GETSEQUENTIALCLUSTERS ) },
{ "FF_ReadClusters", FF_GETMOD_FUNC( FF_READCLUSTERS ) },
{ "FF_ExtendFile", FF_GETMOD_FUNC( FF_EXTENDFILE ) },
{ "FF_WriteClusters", FF_GETMOD_FUNC( FF_WRITECLUSTERS ) },
{ "FF_Read", FF_GETMOD_FUNC( FF_READ ) },
{ "FF_GetC", FF_GETMOD_FUNC( FF_GETC ) },
{ "FF_GetLine", FF_GETMOD_FUNC( FF_GETLINE ) },
{ "FF_Tell", FF_GETMOD_FUNC( FF_TELL ) },
{ "FF_Write", FF_GETMOD_FUNC( FF_WRITE ) },
{ "FF_PutC", FF_GETMOD_FUNC( FF_PUTC ) },
{ "FF_Seek", FF_GETMOD_FUNC( FF_SEEK ) },
{ "FF_Invalidate", FF_GETMOD_FUNC( FF_INVALIDATE ) },
{ "FF_CheckValid", FF_GETMOD_FUNC( FF_CHECKVALID ) },
{ "FF_Close", FF_GETMOD_FUNC( FF_CLOSE ) },
{ "FF_SetTime", FF_GETMOD_FUNC( FF_SETTIME ) },
{ "FF_BytesLeft", FF_GETMOD_FUNC( FF_BYTESLEFT ) },
{ "FF_SetFileTime", FF_GETMOD_FUNC( FF_SETFILETIME ) },
{ "FF_InitBuf", FF_GETMOD_FUNC( FF_INITBUF ) },
/*----- FF_FAT - The FreeRTOS+FAT FAT handling routines */
{ "FF_getFATEntry", FF_GETMOD_FUNC( FF_GETFATENTRY ) },
{ "FF_ClearCluster", FF_GETMOD_FUNC( FF_CLEARCLUSTER ) },
{ "FF_putFATEntry", FF_GETMOD_FUNC( FF_PUTFATENTRY ) },
{ "FF_FindFreeCluster", FF_GETMOD_FUNC( FF_FINDFREECLUSTER ) },
{ "FF_CountFreeClusters", FF_GETMOD_FUNC( FF_COUNTFREECLUSTERS ) },
/*----- FF_UNICODE - The FreeRTOS+FAT hashing routines */
{ "FF_Utf8ctoUtf16c", FF_GETMOD_FUNC( FF_UTF8CTOUTF16C ) },
{ "FF_Utf16ctoUtf8c", FF_GETMOD_FUNC( FF_UTF16CTOUTF8C ) },
{ "FF_Utf32ctoUtf16c", FF_GETMOD_FUNC( FF_UTF32CTOUTF16C ) },
{ "FF_Utf16ctoUtf32c", FF_GETMOD_FUNC( FF_UTF16CTOUTF32C ) },
/*----- FF_FORMAT - The FreeRTOS+FAT format routine */
{ "FF_FormatPartition", FF_GETMOD_FUNC( FF_FORMATPARTITION ) },
/*----- FF_STDIO - The FreeRTOS+FAT stdio front-end */
{ "ff_chmod", FF_GETMOD_FUNC( FF_CHMOD ) },
{ "ff_stat", FF_GETMOD_FUNC( FF_STAT_FUNC ) },
};
#endif /* ffconfigHAS_FUNCTION_TAB */
#define TPASTE2( a, b ) a##b
#if( ffconfigLONG_ERR_MSG != 0 )
/* To get the full error msg: "Not enough memory (malloc( ) returned NULL )" */
#define ERR_ENTRY( M, E ) { M, TPASTE2( FF_ERR_, E ) }
#else
/* To get a shorter msg: "NOT_ENOUGH_MEMORY" */
#define ERR_ENTRY( M, E ) { #E, TPASTE2( FF_ERR_, E ) }
#endif /* ffconfigLONG_ERR_MSG */
const struct _FFERRTAB
{
const char * const strErrorString;
const uint8_t ucErrorCode; /* Currently there are less then 256 errors, so lets keep this table small. */
} xFreeRTOSFATErrorTable[] =
{
{"Unknown or Generic Error!", 1},
ERR_ENTRY( "No Error", NONE ),
ERR_ENTRY( "Null Pointer provided, (probably for IOMAN)", NULL_POINTER ),
ERR_ENTRY( "Not enough memory (malloc() returned NULL)", NOT_ENOUGH_MEMORY ),
ERR_ENTRY( "Device Driver returned a FATAL error!", DEVICE_DRIVER_FAILED ),
ERR_ENTRY( "The blocksize is not 512 multiple", IOMAN_BAD_BLKSIZE ),
ERR_ENTRY( "The memory size, is not a multiple of the blocksize. (Atleast 2 Blocks)", IOMAN_BAD_MEMSIZE ),
ERR_ENTRY( "Device is already registered, use FF_UnregisterBlkDevice() first", IOMAN_DEV_ALREADY_REGD ),
ERR_ENTRY( "No mountable partition was found on the specified device", IOMAN_NO_MOUNTABLE_PARTITION ),
ERR_ENTRY( "The format of the MBR was unrecognised", IOMAN_INVALID_FORMAT ),
ERR_ENTRY( "The provided partition number is out-of-range (0 - 3)", IOMAN_INVALID_PARTITION_NUM ),
ERR_ENTRY( "The selected partition / volume doesn't appear to be FAT formatted", IOMAN_NOT_FAT_FORMATTED ),
ERR_ENTRY( "Cannot register device. (BlkSize not a multiple of 512)", IOMAN_DEV_INVALID_BLKSIZE ),
ERR_ENTRY( "Cannot unregister device, a partition is still mounted", IOMAN_PARTITION_MOUNTED ),
ERR_ENTRY( "Cannot unmount the partition while there are active FILE handles", IOMAN_ACTIVE_HANDLES ),
ERR_ENTRY( "The GPT partition header appears to be corrupt, refusing to mount", IOMAN_GPT_HEADER_CORRUPT ),
ERR_ENTRY( "Disk full", IOMAN_NOT_ENOUGH_FREE_SPACE ),
ERR_ENTRY( "Attempted to Read a sector out of bounds", IOMAN_OUT_OF_BOUNDS_READ ),
ERR_ENTRY( "Attempted to Write a sector out of bounds", IOMAN_OUT_OF_BOUNDS_WRITE ),
ERR_ENTRY( "I/O driver is busy", IOMAN_DRIVER_BUSY ),
ERR_ENTRY( "I/O driver returned fatal error", IOMAN_DRIVER_FATAL_ERROR ),
ERR_ENTRY( "I/O driver returned \"no medium error\"", IOMAN_DRIVER_NOMEDIUM ),
ERR_ENTRY( "Cannot open the file, file already in use", FILE_ALREADY_OPEN ),
ERR_ENTRY( "The specified file could not be found", FILE_NOT_FOUND ),
ERR_ENTRY( "Cannot open a Directory", FILE_OBJECT_IS_A_DIR ),
ERR_ENTRY( "Cannot open for writing: File is marked as Read-Only", FILE_IS_READ_ONLY ),
ERR_ENTRY( "Path not found", FILE_INVALID_PATH ),
ERR_ENTRY( "File operation failed - the file was not opened for writing", FILE_NOT_OPENED_IN_WRITE_MODE ),
ERR_ENTRY( "File operation failed - the file was not opened for reading", FILE_NOT_OPENED_IN_READ_MODE ),
ERR_ENTRY( "File operation failed - could not extend file", FILE_EXTEND_FAILED ),
ERR_ENTRY( "Destination file already exists", FILE_DESTINATION_EXISTS ),
ERR_ENTRY( "Source file was not found", FILE_SOURCE_NOT_FOUND ),
ERR_ENTRY( "Destination path (dir) was not found", FILE_DIR_NOT_FOUND ),
ERR_ENTRY( "Failed to create the directory Entry", FILE_COULD_NOT_CREATE_DIRENT ),
ERR_ENTRY( "A file handle was invalid", FILE_BAD_HANDLE ),
#if( ffconfigREMOVABLE_MEDIA != 0 )
ERR_ENTRY( "File handle got invalid because media was removed", FILE_MEDIA_REMOVED ),
#endif /* ffconfigREMOVABLE_MEDIA */
ERR_ENTRY( "A file or folder of the same name already exists", DIR_OBJECT_EXISTS ),
ERR_ENTRY( "DIR_DIRECTORY_FULL", DIR_DIRECTORY_FULL ),
ERR_ENTRY( "DIR_END_OF_DIR", DIR_END_OF_DIR ),
ERR_ENTRY( "The directory is not empty", DIR_NOT_EMPTY ),
ERR_ENTRY( "Could not extend File or Folder - No Free Space!", FAT_NO_FREE_CLUSTERS ),
ERR_ENTRY( "Could not find the directory specified by the path", DIR_INVALID_PATH ),
ERR_ENTRY( "The Root Dir is full, and cannot be extended on Fat12 or 16 volumes", DIR_CANT_EXTEND_ROOT_DIR ),
ERR_ENTRY( "Not enough space to extend the directory.", DIR_EXTEND_FAILED ),
ERR_ENTRY( "Name exceeds the number of allowed characters for a filename", DIR_NAME_TOO_LONG ),
#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
ERR_ENTRY( "An invalid Unicode character was provided!", UNICODE_INVALID_CODE ),
ERR_ENTRY( "Not enough space in the UTF-16 buffer to encode the entire sequence", UNICODE_DEST_TOO_SMALL ),
ERR_ENTRY( "An invalid UTF-16 sequence was encountered", UNICODE_INVALID_SEQUENCE ),
ERR_ENTRY( "Filename exceeds MAX long-filename length when converted to UTF-16", UNICODE_CONVERSION_EXCEEDED ),
#endif /* ffconfigUNICODE_UTF16_SUPPORT */
};
/**
* @public
* @brief Returns a pointer to a string relating to a FreeRTOS+FAT error code.
*
* @param iErrorCode The error code.
*
* @return Pointer to a string describing the error.
*
**/
const char *FF_GetErrMessage( FF_Error_t iErrorCode )
{
uint32_t stCount = ARRAY_SIZE( xFreeRTOSFATErrorTable );
while( stCount-- )
{
if( ( ( UBaseType_t )xFreeRTOSFATErrorTable[ stCount ].ucErrorCode ) == FF_GETERROR( iErrorCode ) )
{
break;
}
}
return xFreeRTOSFATErrorTable[ stCount ].strErrorString;
}
const char *FF_GetErrModule( FF_Error_t iErrorCode )
{
uint32_t stCount = ARRAY_SIZE( xFreeRTOSFATModuleTable );
while( stCount-- )
{
if( xFreeRTOSFATModuleTable[ stCount ].ucModuleID == ( uint8_t )FF_GETMODULE( iErrorCode ) )
{
break;
}
}
return xFreeRTOSFATModuleTable[ stCount ].strModuleName;
}
#if( ffconfigHAS_FUNCTION_TAB != 0 )
const char *FF_GetErrFunction( FF_Error_t iErrorCode )
{
uint32_t stCount = ARRAY_SIZE( xFreeRTOSFATFunctionTable );
uint16_t ModuleFunc = FF_GETMOD_FUNC( iErrorCode );
static char funcCode[32];
while( stCount-- != 0 )
{
if( xFreeRTOSFATFunctionTable[ stCount ].ucFunctionID == ModuleFunc )
{
return xFreeRTOSFATFunctionTable[ stCount ].strFunctionName;
}
}
snprintf( funcCode, sizeof( funcCode ), "Func %X", ModuleFunc );
return ( const char * )funcCode;
}
#endif /* ffconfigHAS_FUNCTION_TAB */
const char *FF_GetErrDescription( FF_Error_t iErrorCode, char *apBuf, int aMaxlen )
{
if( FF_isERR( iErrorCode ) )
{
#if( ffconfigHAS_FUNCTION_TAB != 0 )
snprintf (apBuf, ( size_t ) aMaxlen, "%s::%s::%s",
FF_GetErrModule( iErrorCode ),
FF_GetErrFunction( iErrorCode ),
FF_GetErrMessage( iErrorCode ));
#else
snprintf (apBuf, ( size_t ) aMaxlen, "%s::%s",
FF_GetErrModule( iErrorCode ),
FF_GetErrMessage( iErrorCode ));
#endif /* ffconfigHAS_FUNCTION_TAB */
}
else
{
snprintf (apBuf, ( size_t ) aMaxlen, "No error");
}
return apBuf;
}
#endif /* ffconfigDEBUG != 0 */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,753 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
/**
* @file ff_format.c
* @ingroup FORMAT
*
* @defgroup FAT Fat File-System
* @brief Format a drive, given the number of sectors.
*
**/
#include "ff_headers.h"
#include <time.h>
#include <string.h>
#if defined( __BORLANDC__ )
#include "ff_windows.h"
#else
#include "FreeRTOS.h"
#include "task.h" /* For FreeRTOS date/time function */
#endif
/*=========================================================================================== */
#define OFS_PART_ACTIVE_8 0x000 /* 0x01BE 0x80 if active */
#define OFS_PART_START_HEAD_8 0x001 /* 0x01BF */
#define OFS_PART_START_SEC_TRACK_16 0x002 /* 0x01C0 */
#define OFS_PART_ID_NUMBER_8 0x004 /* 0x01C2 */
#define OFS_PART_ENDING_HEAD_8 0x005 /* 0x01C3 */
#define OFS_PART_ENDING_SEC_TRACK_16 0x006 /* 0x01C4 = SectorCount - 1 - ulHiddenSectors */
#define OFS_PART_STARTING_LBA_32 0x008 /* 0x01C6 = ulHiddenSectors (This is important) */
#define OFS_PART_LENGTH_32 0x00C /* 0x01CA = SectorCount - 1 - ulHiddenSectors */
#define OFS_PTABLE_MACH_CODE 0x000 /* 0x0000 */
#define OFS_PTABLE_PART_0 0x1BE /* 446 */
#define OFS_PTABLE_PART_1 0x1CE /* 462 */
#define OFS_PTABLE_PART_2 0x1DE /* 478 */
#define OFS_PTABLE_PART_3 0x1FE /* 494 */
#define OFS_PTABLE_PART_LEN 16
/*=========================================================================================== */
#define OFS_BPB_jmpBoot_24 0x000 /* uchar jmpBoot[3] "0xEB 0x00 0x90" */
#define OFS_BPB_OEMName_64 0x003 /* uchar BS_OEMName[8] "MSWIN4.1" */
#define OFS_BPB_BytsPerSec_16 0x00B /* Only 512, 1024, 2048 or 4096 */
#define OFS_BPB_SecPerClus_8 0x00D /* Only 1, 2, 4, 8, 16, 32, 64, 128 */
#define OFS_BPB_ResvdSecCnt_16 0x00E /* ulFATReservedSectors, e.g. 1 (FAT12/16) or 32 (FAT32) */
#define OFS_BPB_NumFATs_8 0x010 /* 2 recommended */
#define OFS_BPB_RootEntCnt_16 0x011 /* ((iFAT16RootSectors * 512) / 32) 512 (FAT12/16) or 0 (FAT32) */
#define OFS_BPB_TotSec16_16 0x013 /* xxx (FAT12/16) or 0 (FAT32) */
#define OFS_BPB_Media_8 0x015 /* 0xF0 (rem media) also in FAT[0] low byte */
#define OFS_BPB_FATSz16_16 0x016
#define OFS_BPB_SecPerTrk_16 0x018 /* n.a. CF has no tracks */
#define OFS_BPB_NumHeads_16 0x01A /* n.a. 1 ? */
#define OFS_BPB_HiddSec_32 0x01C /* n.a. 0 for nonparitioned volume */
#define OFS_BPB_TotSec32_32 0x020 /* >= 0x10000 */
#define OFS_BPB_16_DrvNum_8 0x024 /* n.a. */
#define OFS_BPB_16_Reserved1_8 0x025 /* n.a. */
#define OFS_BPB_16_BootSig_8 0x026 /* n.a. */
#define OFS_BPB_16_BS_VolID_32 0x027 /* "unique" number */
#define OFS_BPB_16_BS_VolLab_88 0x02B /* "NO NAME " */
#define OFS_BPB_16_FilSysType_64 0x036 /* "FAT12 " */
#define OFS_BPB_32_FATSz32_32 0x024 /* Only when BPB_FATSz16 = 0 */
#define OFS_BPB_32_ExtFlags_16 0x028 /* FAT32 only */
#define OFS_BPB_32_FSVer_16 0x02A /* 0:0 */
#define OFS_BPB_32_RootClus_32 0x02C /* See 'iFAT32RootClusters' Normally 2 */
#define OFS_BPB_32_FSInfo_16 0x030 /* Normally 1 */
#define OFS_BPB_32_BkBootSec_16 0x032 /* Normally 6 */
#define OFS_BPB_32_Reserved_96 0x034 /* Zeros */
#define OFS_BPB_32_DrvNum_8 0x040 /* n.a. */
#define OFS_BPB_32_Reserved1_8 0x041 /* n.a. */
#define OFS_BPB_32_BootSig_8 0x042 /* n.a. */
#define OFS_BPB_32_VolID_32 0x043 /* "unique" number */
#define OFS_BPB_32_VolLab_88 0x047 /* "NO NAME " */
#define OFS_BPB_32_FilSysType_64 0x052 /* "FAT12 " */
#define OFS_FSI_32_LeadSig 0x000 /* With contents 0x41615252 */
#define OFS_FSI_32_Reserved1 0x004 /* 480 times 0 */
#define OFS_FSI_32_StrucSig 0x1E4 /* With contents 0x61417272 */
#define OFS_FSI_32_Free_Count 0x1E8 /* last known free cluster count on the volume, ~0 for unknown */
#define OFS_FSI_32_Nxt_Free 0x1EC /* cluster number at which the driver should start looking for free clusters */
#define OFS_FSI_32_Reserved2 0x1F0 /* zero's */
#define OFS_FSI_32_TrailSig 0x1FC /* 0xAA550000 (little endian) */
#define RESV_COUNT 32
#ifdef ffconfigMIN_CLUSTERS_FAT32
#define MIN_CLUSTER_COUNT_FAT32 ffconfigMIN_CLUSTERS_FAT32
#else
#define MIN_CLUSTER_COUNT_FAT32 ( 65525 )
#endif
#ifdef ffconfigMIN_CLUSTERS_FAT16
#define MIN_CLUSTERS_FAT16 ffconfigMIN_CLUSTERS_FAT16
#else
#define MIN_CLUSTERS_FAT16 ( 4085 + 1 )
#endif
#ifndef ffconfigFAT16_ROOT_SECTORS
#define ffconfigFAT16_ROOT_SECTORS 32
#endif
static portINLINE uint32_t ulMin32( uint32_t a, uint32_t b )
{
uint32_t ulReturn;
if( a <= b )
{
ulReturn = a;
}
else
{
ulReturn = b;
}
return ulReturn;
}
/*_RB_ Candidate for splitting into multiple functions? */
FF_Error_t FF_Format( FF_Disk_t *pxDisk, BaseType_t xPartitionNumber, BaseType_t xPreferFAT16, BaseType_t xSmallClusters )
{
uint32_t ulHiddenSectors; /* Space from MBR and partition table */
const uint32_t ulFSInfo = 1; /* Sector number of FSINFO structure within the reserved area */
const uint32_t ulBackupBootSector = 6; /* Sector number of "copy of the boot record" within the reserved area */
const BaseType_t xFATCount = 2; /* Number of FAT's */
uint32_t ulFATReservedSectors = 0; /* Space between the partition table and FAT table. */
int32_t iFAT16RootSectors = 0; /* Number of sectors reserved for root directory (FAT16 only) */
int32_t iFAT32RootClusters = 0; /* Initial amount of clusters claimed for root directory (FAT32 only) */
uint8_t ucFATType = 0; /* Either 'FF_T_FAT16' or 'FF_T_FAT32' */
uint32_t ulVolumeID = 0; /* A pseudo Volume ID */
uint32_t ulSectorsPerFAT = 0; /* Number of sectors used by a single FAT table */
uint32_t ulClustersPerFATSector = 0; /* # of clusters which can be described within a sector (256 or 128) */
uint32_t ulSectorsPerCluster = 0; /* Size of a cluster (# of sectors) */
uint32_t ulUsableDataSectors = 0; /* Usable data sectors (= SectorCount - (ulHiddenSectors + ulFATReservedSectors)) */
uint32_t ulUsableDataClusters = 0; /* equals "ulUsableDataSectors / ulSectorsPerCluster" */
uint32_t ulNonDataSectors = 0; /* ulFATReservedSectors + ulHiddenSectors + iFAT16RootSectors */
uint32_t ulClusterBeginLBA = 0; /* Sector address of the first data cluster */
uint32_t ulSectorCount;
uint8_t *pucSectorBuffer = 0;
FF_SPartFound_t xPartitionsFound;
FF_Part_t *pxMyPartition = 0;
FF_IOManager_t *pxIOManager = pxDisk->pxIOManager;
FF_PartitionSearch( pxIOManager, &xPartitionsFound );
if( xPartitionNumber >= xPartitionsFound.iCount )
{
return FF_ERR_IOMAN_INVALID_PARTITION_NUM | FF_MODULE_FORMAT;
}
pxMyPartition = xPartitionsFound.pxPartitions + xPartitionNumber;
ulSectorCount = pxMyPartition->ulSectorCount;
ulHiddenSectors = pxMyPartition->ulStartLBA;
if( ( ( xPreferFAT16 == pdFALSE ) && ( ( ulSectorCount - RESV_COUNT ) >= 65536 ) ) ||
( ( ulSectorCount - RESV_COUNT ) >= ( 64 * MIN_CLUSTER_COUNT_FAT32 ) ) )
{
ucFATType = FF_T_FAT32;
iFAT32RootClusters = 2;
ulFATReservedSectors = RESV_COUNT;
iFAT16RootSectors = 0;
}
else
{
ucFATType = FF_T_FAT16;
iFAT32RootClusters = 0;
ulFATReservedSectors = 1u;
iFAT16RootSectors = ffconfigFAT16_ROOT_SECTORS; /* 32 sectors to get 512 dir entries */
}
/* Set start sector and length to allow FF_BlockRead/Write */
pxIOManager->xPartition.ulTotalSectors = pxMyPartition->ulSectorCount;
pxIOManager->xPartition.ulBeginLBA = pxMyPartition->ulStartLBA;
/* TODO: Find some solution here to get a unique disk ID */
ulVolumeID = ( rand() << 16 ) | rand(); /*_RB_ rand() has proven problematic in some environments. */
/* Sectors within partition which can not be used */
ulNonDataSectors = ulFATReservedSectors + iFAT16RootSectors;
/* A fs dependent constant: */
if( ucFATType == FF_T_FAT32 )
{
/* In FAT32, 4 bytes are needed to store the address (LBA) of a cluster.
Each FAT sector of 512 bytes can contain 512 / 4 = 128 entries. */
ulClustersPerFATSector = 128u;
}
else
{
/* In FAT16, 2 bytes are needed to store the address (LBA) of a cluster.
Each FAT sector of 512 bytes can contain 512 / 2 = 256 entries. */
ulClustersPerFATSector = 256u;
}
FF_PRINTF( "FF_Format: Secs %lu Rsvd %lu Hidden %lu Root %lu Data %lu\n",
ulSectorCount, ulFATReservedSectors, ulHiddenSectors, iFAT16RootSectors, ulSectorCount - ulNonDataSectors );
/* Either search from small to large or v.v. */
if( xSmallClusters != 0 )
{
/* The caller prefers to have small clusters.
Less waste but it can be slower. */
ulSectorsPerCluster = 1u;
}
else
{
if( ucFATType == FF_T_FAT32 )
{
ulSectorsPerCluster = 64u;
}
else
{
ulSectorsPerCluster = 32u;
}
}
for( ;; )
{
int32_t groupSize;
/* Usable sectors */
ulUsableDataSectors = ulSectorCount - ulNonDataSectors;
/* Each group consists of 'xFATCount' sectors + 'ulClustersPerFATSector' clusters */
groupSize = xFATCount + ulClustersPerFATSector * ulSectorsPerCluster;
/* This amount of groups will fit: */
ulSectorsPerFAT = ( ulUsableDataSectors + groupSize - ulSectorsPerCluster - xFATCount ) / groupSize;
ulUsableDataClusters = ulMin32(
( uint32_t ) ( ulUsableDataSectors - xFATCount * ulSectorsPerFAT ) / ulSectorsPerCluster,
( uint32_t ) ( ulClustersPerFATSector * ulSectorsPerFAT ) );
ulUsableDataSectors = ulUsableDataClusters * ulSectorsPerCluster;
if( ( ucFATType == FF_T_FAT16 ) && ( ulUsableDataClusters >= MIN_CLUSTERS_FAT16 ) && ( ulUsableDataClusters < 65536 ) )
{
break;
}
if( ( ucFATType == FF_T_FAT32 ) && ( ulUsableDataClusters >= 65536 ) && ( ulUsableDataClusters < 0x0FFFFFEF ) )
{
break;
}
/* Was this the last test? */
if( ( ( xSmallClusters != pdFALSE ) && ( ulSectorsPerCluster == 32 ) ) ||
( ( xSmallClusters == pdFALSE ) && ( ulSectorsPerCluster == 1) ) )
{
FF_PRINTF( "FF_Format: Can not make a FAT%d (tried %d) with %lu sectors\n",
ucFATType == FF_T_FAT32 ? 32 : 16, xPreferFAT16 ? 16 : 32, ulSectorCount );
return FF_ERR_IOMAN_BAD_MEMSIZE | FF_MODULE_FORMAT;
}
/* No it wasn't, try next clustersize */
if( xSmallClusters != pdFALSE )
{
ulSectorsPerCluster <<= 1;
}
else
{
ulSectorsPerCluster >>= 1;
}
}
if( ( ucFATType == FF_T_FAT32 ) && ( ulSectorCount >= 0x100000UL ) ) /* Larger than 0.5 GB */
{
uint32_t ulRemaining;
/*
* Putting the FAT-table into the second 4MB erase block gives
* a higher performance and a longer life-time.
* See e.g. here:
* http://3gfp.com/wp/2014/07/formatting-sd-cards-for-speed-and-lifetime/
*/
ulFATReservedSectors = 8192 - ulHiddenSectors;
ulNonDataSectors = ulFATReservedSectors + iFAT16RootSectors;
ulRemaining = (ulNonDataSectors + 2 * ulSectorsPerFAT) % 128;
if( ulRemaining != 0 )
{
/* In order to get ClusterBeginLBA well aligned (on a 128 sector boundary) */
ulFATReservedSectors += ( 128 - ulRemaining );
ulNonDataSectors = ulFATReservedSectors + iFAT16RootSectors;
}
ulUsableDataSectors = ulSectorCount - ulNonDataSectors - 2 * ulSectorsPerFAT;
ulUsableDataClusters = ulUsableDataSectors / ulSectorsPerCluster;
}
ulClusterBeginLBA = ulHiddenSectors + ulFATReservedSectors + 2 * ulSectorsPerFAT;
pucSectorBuffer = ( uint8_t * ) ffconfigMALLOC( 512 );
if( pucSectorBuffer == NULL )
{
return FF_ERR_NOT_ENOUGH_MEMORY | FF_MODULE_FORMAT;
}
/* ======================================================================================= */
memset( pucSectorBuffer, '\0', 512 );
memcpy( pucSectorBuffer + OFS_BPB_jmpBoot_24, "\xEB\x00\x90" "FreeRTOS", 11 ); /* Includes OFS_BPB_OEMName_64 */
FF_putShort( pucSectorBuffer, OFS_BPB_BytsPerSec_16, 512 ); /* 0x00B / Only 512, 1024, 2048 or 4096 */
FF_putShort( pucSectorBuffer, OFS_BPB_ResvdSecCnt_16, ( uint32_t ) ulFATReservedSectors ); /* 0x00E / 1 (FAT12/16) or 32 (FAT32) */
FF_putChar( pucSectorBuffer, OFS_BPB_NumFATs_8, 2); /* 0x010 / 2 recommended */
FF_putShort( pucSectorBuffer, OFS_BPB_RootEntCnt_16, ( uint32_t ) ( iFAT16RootSectors * 512 ) / 32 ); /* 0x011 / 512 (FAT12/16) or 0 (FAT32) */
/* For FAT12 and FAT16 volumes, this field contains the count of 32- */
/* byte directory entries in the root directory */
FF_putChar( pucSectorBuffer, OFS_BPB_Media_8, 0xF8 ); /* 0x015 / 0xF0 (rem media) also in FAT[0] low byte */
FF_putShort( pucSectorBuffer, OFS_BPB_SecPerTrk_16, 0x3F ); /* 0x18 n.a. CF has no tracks */
FF_putShort( pucSectorBuffer, OFS_BPB_NumHeads_16, 255 ); /* 0x01A / n.a. 1 ? */
FF_putLong (pucSectorBuffer, OFS_BPB_HiddSec_32, ( uint32_t ) ulHiddenSectors ); /* 0x01C / n.a. 0 for nonparitioned volume */
{
int32_t fatBeginLBA;
int32_t dirBegin;
FF_putChar( pucSectorBuffer, OFS_BPB_SecPerClus_8, ( uint32_t ) ulSectorsPerCluster ); /* 0x00D / Only 1, 2, 4, 8, 16, 32, 64, 128 */
FF_PRINTF("FF_Format: SecCluster %lu DatSec %lu DataClus %lu ulClusterBeginLBA %lu\n",
ulSectorsPerCluster, ulUsableDataSectors, ulUsableDataClusters, ulClusterBeginLBA );
/* This field is the new 32-bit total count of sectors on the volume. */
/* This count includes the count of all sectors in all four regions of the volume */
FF_putShort( pucSectorBuffer, OFS_BPB_TotSec16_16, 0 ); /* 0x013 / xxx (FAT12/16) or 0 (FAT32) */
FF_putLong (pucSectorBuffer, OFS_BPB_TotSec32_32, ulSectorCount ); /* 0x020 / >= 0x10000 */
if( ucFATType == FF_T_FAT32 )
{
FF_putLong( pucSectorBuffer, OFS_BPB_32_FATSz32_32, ulSectorsPerFAT ); /* 0x24 / Only when BPB_FATSz16 = 0 */
FF_putShort( pucSectorBuffer, OFS_BPB_32_ExtFlags_16, 0 ); /* 0x28 / FAT32 only */
FF_putShort( pucSectorBuffer, OFS_BPB_32_FSVer_16, 0 ); /* 0x2A / 0:0 */
FF_putLong( pucSectorBuffer, OFS_BPB_32_RootClus_32, ( uint32_t ) iFAT32RootClusters ); /* 0x2C / Normally 2 */
FF_putShort( pucSectorBuffer, OFS_BPB_32_FSInfo_16, ulFSInfo ); /* 0x30 / Normally 1 */
FF_putShort( pucSectorBuffer, OFS_BPB_32_BkBootSec_16, ulBackupBootSector ); /* 0x32 / Normally 6 */
FF_putChar( pucSectorBuffer, OFS_BPB_32_DrvNum_8, 0 ); /* 0x40 / n.a. */
FF_putChar( pucSectorBuffer, OFS_BPB_32_BootSig_8, 0x29 ); /* 0x42 / n.a. */
FF_putLong( pucSectorBuffer, OFS_BPB_32_VolID_32, ( uint32_t ) ulVolumeID ); /* 0x43 / "unique" number */
memcpy( pucSectorBuffer + OFS_BPB_32_VolLab_88, "MY NAME ", 11 ); /* 0x47 / "NO NAME " */
memcpy( pucSectorBuffer + OFS_BPB_32_FilSysType_64, "FAT32 ", 8 ); /* 0x52 / "FAT12 " */
}
else
{
FF_putChar( pucSectorBuffer, OFS_BPB_16_DrvNum_8, 0u ); /* 0x024 / n.a. */
FF_putChar( pucSectorBuffer, OFS_BPB_16_Reserved1_8, 0 ); /* 0x025 / n.a. */
FF_putChar( pucSectorBuffer, OFS_BPB_16_BootSig_8, 0x29 ); /* 0x026 / n.a. */
FF_putLong (pucSectorBuffer, OFS_BPB_16_BS_VolID_32, ( uint32_t ) ulVolumeID ); /* 0x027 / "unique" number */
FF_putShort( pucSectorBuffer, OFS_BPB_FATSz16_16, ulSectorsPerFAT ); /* 0x16 */
memcpy( pucSectorBuffer + OFS_BPB_16_BS_VolLab_88, "MY NAME ", 11 ); /* 0x02B / "NO NAME " */
memcpy( pucSectorBuffer + OFS_BPB_16_FilSysType_64, "FAT16 ", 8 ); /* 0x036 / "FAT12 " */
}
pucSectorBuffer[510] = 0x55;
pucSectorBuffer[511] = 0xAA;
FF_BlockWrite( pxIOManager, ulHiddenSectors, 1, pucSectorBuffer, 0u );
if (ucFATType == FF_T_FAT32)
{
FF_BlockWrite( pxIOManager, ulHiddenSectors + ulBackupBootSector, 1, pucSectorBuffer, pdFALSE );
}
if( ucFATType == FF_T_FAT32 )
{
memset( pucSectorBuffer, '\0', 512 );
FF_putLong( pucSectorBuffer, OFS_FSI_32_LeadSig, 0x41615252 ); /* to validate that this is in fact an FSInfo sector. */
/* OFS_FSI_32_Reserved1 0x004 / 480 times 0 */
FF_putLong( pucSectorBuffer, OFS_FSI_32_StrucSig, 0x61417272 ); /* Another signature that is more localized in the */
/* sector to the location of the fields that are used. */
FF_putLong( pucSectorBuffer, OFS_FSI_32_Free_Count, ulUsableDataClusters ); /* last known free cluster count on the volume, ~0 for unknown */
FF_putLong( pucSectorBuffer, OFS_FSI_32_Nxt_Free, 2 ); /* cluster number at which the driver should start looking for free clusters */
/* OFS_FSI_32_Reserved2 0x1F0 / zero's */
FF_putLong( pucSectorBuffer, OFS_FSI_32_TrailSig, 0xAA550000 ); /* Will correct for endianness */
FF_BlockWrite( pxIOManager, ulHiddenSectors + ulFSInfo, 1, pucSectorBuffer, pdFALSE );
FF_BlockWrite( pxIOManager, ulHiddenSectors + ulFSInfo + ulBackupBootSector, 1, pucSectorBuffer, pdFALSE );
}
fatBeginLBA = ulHiddenSectors + ulFATReservedSectors;
memset( pucSectorBuffer, '\0', 512 );
switch( ucFATType )
{
case FF_T_FAT16:
FF_putShort( pucSectorBuffer, 0, 0xFFF8 ); /* First FAT entry. */
FF_putShort( pucSectorBuffer, 2, 0xFFFF ); /* RESERVED alloc. */
break;
case FF_T_FAT32:
FF_putLong( pucSectorBuffer, 0, 0x0FFFFFF8 ); /* FAT32 FAT sig. */
FF_putLong( pucSectorBuffer, 4, 0xFFFFFFFF ); /* RESERVED alloc. */
FF_putLong( pucSectorBuffer, 8, 0x0FFFFFFF ); /* Root dir allocation. */
break;
default:
break;
}
FF_BlockWrite( pxIOManager, ( uint32_t ) fatBeginLBA, 1, pucSectorBuffer, pdFALSE );
FF_BlockWrite( pxIOManager, ( uint32_t ) fatBeginLBA + ulSectorsPerFAT, 1, pucSectorBuffer, pdFALSE );
FF_PRINTF( "FF_Format: Clearing entire FAT (2 x %lu sectors):\n", ulSectorsPerFAT );
{
int32_t addr;
memset( pucSectorBuffer, '\0', 512 );
for( addr = fatBeginLBA+1;
addr < ( fatBeginLBA + ( int32_t ) ulSectorsPerFAT );
addr++ )
{
FF_BlockWrite( pxIOManager, ( uint32_t ) addr, 1, pucSectorBuffer, pdFALSE );
FF_BlockWrite( pxIOManager, ( uint32_t ) addr + ulSectorsPerFAT, 1, pucSectorBuffer, pdFALSE );
}
}
FF_PRINTF( "FF_Format: Clearing done\n" );
dirBegin = fatBeginLBA + ( 2 * ulSectorsPerFAT );
#if( ffconfigTIME_SUPPORT != 0 )
{
FF_SystemTime_t str_t;
int16_t myShort;
FF_GetSystemTime( &str_t );
myShort = ( ( str_t.Hour << 11 ) & 0xF800 ) |
( ( str_t.Minute << 5 ) & 0x07E0 ) |
( ( str_t.Second / 2 ) & 0x001F );
FF_putShort( pucSectorBuffer, 22, ( uint32_t ) myShort );
myShort = ( ( ( str_t.Year- 1980 ) << 9 ) & 0xFE00 ) |
( ( str_t.Month << 5 ) & 0x01E0 ) |
( str_t.Day & 0x001F );
FF_putShort( pucSectorBuffer, 24, ( uint32_t ) myShort);
}
#endif /* ffconfigTIME_SUPPORT */
memcpy (pucSectorBuffer, "MY_DISK ", 11);
pucSectorBuffer[11] = FF_FAT_ATTR_VOLID;
{
int32_t lAddress;
int32_t lLastAddress;
if( iFAT16RootSectors != 0 )
{
lLastAddress = dirBegin + iFAT16RootSectors;
}
else
{
lLastAddress = dirBegin + ulSectorsPerCluster;
}
FF_PRINTF("FF_Format: Clearing root directory at %08lX: %lu sectors\n", dirBegin, lLastAddress - dirBegin );
for( lAddress = dirBegin; lAddress < lLastAddress; lAddress++ )
{
FF_BlockWrite( pxIOManager, ( uint32_t ) lAddress, 1, pucSectorBuffer, 0u );
if( lAddress == dirBegin )
{
memset( pucSectorBuffer, '\0', 512 );
}
}
}
}
ffconfigFREE( pucSectorBuffer );
return FF_ERR_NONE;
}
FF_Error_t FF_Partition( FF_Disk_t *pxDisk, FF_PartitionParameters_t *pParams )
{
const uint32_t ulInterSpace = pParams->ulInterSpace ? pParams->ulInterSpace : 2048; /* Hidden space between 2 extended partitions */
BaseType_t xPartitionNumber;
FF_Part_t pxPartitions[ ffconfigMAX_PARTITIONS ];
uint32_t ulPartitionOffset; /* Pointer within partition table */
FF_Buffer_t *pxSectorBuffer;
uint8_t *pucBuffer;
uint32_t ulSummedSizes = 0; /* Summed sizes as a percentage or as number of sectors. */
BaseType_t xPartitionCount = 0;
BaseType_t xNeedExtended;
uint32_t ulReservedSpace;
uint32_t ulAvailable;
FF_IOManager_t *pxIOManager = pxDisk->pxIOManager;
/* Clear caching without flushing first. */
FF_IOMAN_InitBufferDescriptors( pxIOManager );
/* Avoid sanity checks by FF_BlockRead/Write. */
pxIOManager->xPartition.ulTotalSectors = 0;
/* Get the sum of sizes and number of actual partitions. */
for( xPartitionNumber = 0; xPartitionNumber < ffconfigMAX_PARTITIONS; xPartitionNumber++ )
{
if( pParams->xSizes[ xPartitionNumber ] > 0 )
{
xPartitionCount++;
ulSummedSizes += pParams->xSizes[ xPartitionNumber ];
}
}
if( xPartitionCount == 0 )
{
xPartitionCount = 1;
if( pParams->eSizeType == eSizeIsSectors)
{
pParams->xSizes[ 0 ] = pParams->ulSectorCount;
}
else
{
pParams->xSizes[ 0 ] = 100;
}
ulSummedSizes = pParams->xSizes[ 0 ];
}
/* Correct PrimaryCount if necessary. */
if( pParams->xPrimaryCount > ( ( xPartitionCount > 4 ) ? 3 : xPartitionCount ) )
{
pParams->xPrimaryCount = ( xPartitionCount > 4 ) ? 3 : xPartitionCount;
}
/* Now see if extended is necessary. */
xNeedExtended = ( xPartitionCount > pParams->xPrimaryCount );
if( xNeedExtended != pdFALSE )
{
if( pParams->ulHiddenSectors < 4096 )
{
pParams->ulHiddenSectors = 4096;
}
ulReservedSpace = ulInterSpace * ( xPartitionCount - pParams->xPrimaryCount );
}
else
{
/* There must be at least 1 hidden sector. */
if( pParams->ulHiddenSectors < 1 )
{
pParams->ulHiddenSectors = 1;
}
ulReservedSpace = 0;
}
ulAvailable = pParams->ulSectorCount - pParams->ulHiddenSectors - ulReservedSpace;
/* Check validity of Sizes */
switch( pParams->eSizeType )
{
case eSizeIsQuota: /* Assign a quotum (sum of Sizes is free, all disk space will be allocated) */
break;
case eSizeIsPercent: /* Assign a percentage of the available space (sum of Sizes must be <= 100) */
if( ulSummedSizes > 100 )
{
return FF_FORMATPARTITION | FF_ERR_IOMAN_BAD_MEMSIZE;
}
ulSummedSizes = 100;
break;
case eSizeIsSectors: /* Assign fixed number of sectors (512 byte each) */
if( ulSummedSizes > ulAvailable )
{
return FF_FORMATPARTITION | FF_ERR_IOMAN_BAD_MEMSIZE;
}
break;
}
{
uint32_t ulRemaining = ulAvailable;
uint32_t ulLBA = pParams->ulHiddenSectors;
/* Divide the available sectors among the partitions: */
memset( pxPartitions, '\0', sizeof( pxPartitions ) );
for( xPartitionNumber = 0; xPartitionNumber < xPartitionCount; xPartitionNumber++ )
{
if( pParams->xSizes[ xPartitionNumber ] > 0 )
{
uint32_t ulSize;
switch( pParams->eSizeType )
{
case eSizeIsQuota: /* Assign a quotum (sum of Sizes is free, all disk space will be allocated) */
case eSizeIsPercent: /* Assign a percentage of the available space (sum of Sizes must be <= 100) */
ulSize = ( uint32_t ) ( ( ( uint64_t ) pParams->xSizes[ xPartitionNumber ] * ulAvailable) / ulSummedSizes );
break;
case eSizeIsSectors: /* Assign fixed number of sectors (512 byte each) */
default: /* Just for the compiler(s) */
ulSize = pParams->xSizes[ xPartitionNumber ];
break;
}
if( ulSize > ulRemaining )
{
ulSize = ulRemaining;
}
ulRemaining -= ulSize;
pxPartitions[ xPartitionNumber ].ulSectorCount = ulSize;
pxPartitions[ xPartitionNumber ].ucActive = 0x80;
pxPartitions[ xPartitionNumber ].ulStartLBA = ulLBA; /* ulStartLBA might still change for logical partitions */
pxPartitions[ xPartitionNumber ].ucPartitionID = 0x0B;
ulLBA += ulSize;
}
}
}
if( xNeedExtended != pdFALSE )
{
/* Create at least 1 extended/logical partition */
int index;
/* Start of the big extended partition */
unsigned extendedLBA = pParams->ulHiddenSectors;
/* Where to write the table */
uint32_t ulLBA = 0;
/* Contents of the table */
FF_Part_t writeParts[4];
for( index = -1; index < xPartitionCount; index++ )
{
uint32_t ulNextLBA;
memset (writeParts, '\0', sizeof( writeParts ) );
if( index < 0 )
{
/* we're at secor 0: */
/* write primary partitions, if any */
/* create big extended partition */
uint32_t ulStartLBA = pParams->ulHiddenSectors;
for( xPartitionNumber = 0; xPartitionNumber < pParams->xPrimaryCount; xPartitionNumber++ )
{
writeParts[ xPartitionNumber ].ulStartLBA = ulStartLBA;
writeParts[ xPartitionNumber ].ulSectorCount = pxPartitions[ xPartitionNumber ].ulSectorCount;
writeParts[ xPartitionNumber ].ucActive = 0x80;
writeParts[ xPartitionNumber ].ucPartitionID = 0x0B;
ulStartLBA += writeParts[ xPartitionNumber ].ulSectorCount;
index++;
}
extendedLBA = ulStartLBA;
writeParts[ xPartitionNumber ].ulStartLBA = ulStartLBA;
writeParts[ xPartitionNumber ].ulSectorCount = pParams->ulSectorCount - ulStartLBA;
writeParts[ xPartitionNumber ].ucActive = 0x80;
writeParts[ xPartitionNumber ].ucPartitionID = 0x05;
ulNextLBA = ulStartLBA;
}
else
{
/* Create a logical partition with "ulSectorCount" sectors: */
writeParts[ 0 ].ulStartLBA = ulInterSpace;
writeParts[ 0 ].ulSectorCount = pxPartitions[index].ulSectorCount;
writeParts[ 0 ].ucActive = 0x80;
writeParts[ 0 ].ucPartitionID = 0x0B;
if( index < xPartitionCount - 1 )
{
/* Next extended partition */
writeParts[ 1 ].ulStartLBA = ulInterSpace + ulLBA - extendedLBA + writeParts[ 0 ].ulSectorCount;
writeParts[ 1 ].ulSectorCount = pxPartitions[index+1].ulSectorCount + ulInterSpace;
writeParts[ 1 ].ucActive = 0x80;
writeParts[ 1 ].ucPartitionID = 0x05;
}
ulNextLBA = writeParts[ 1 ].ulStartLBA + extendedLBA;
}
pxSectorBuffer = FF_GetBuffer(pxIOManager, ( uint32_t ) ulLBA, ( uint8_t ) FF_MODE_WRITE );
{
if( pxSectorBuffer == NULL )
{
return FF_ERR_DEVICE_DRIVER_FAILED;
}
}
pucBuffer = pxSectorBuffer->pucBuffer;
memset ( pucBuffer, 0, 512 );
memcpy ( pucBuffer + OFS_BPB_jmpBoot_24, "\xEB\x00\x90" "FreeRTOS", 11 ); /* Includes OFS_BPB_OEMName_64 */
ulPartitionOffset = OFS_PTABLE_PART_0;
for( xPartitionNumber = 0; xPartitionNumber < ffconfigMAX_PARTITIONS; xPartitionNumber++, ulPartitionOffset += 16 )
{
FF_putChar( pucBuffer, ulPartitionOffset + OFS_PART_ACTIVE_8, writeParts[ xPartitionNumber ].ucActive ); /* 0x01BE 0x80 if active */
FF_putChar( pucBuffer, ulPartitionOffset + OFS_PART_START_HEAD_8, 1 ); /* 0x001 / 0x01BF */
FF_putShort(pucBuffer, ulPartitionOffset + OFS_PART_START_SEC_TRACK_16, 1 ); /* 0x002 / 0x01C0 */
FF_putChar( pucBuffer, ulPartitionOffset + OFS_PART_ID_NUMBER_8, writeParts[ xPartitionNumber ].ucPartitionID );/* 0x004 / 0x01C2 */
FF_putChar( pucBuffer, ulPartitionOffset + OFS_PART_ENDING_HEAD_8, 0xFE ); /* 0x005 / 0x01C3 */
FF_putShort(pucBuffer, ulPartitionOffset + OFS_PART_ENDING_SEC_TRACK_16, writeParts[ xPartitionNumber ].ulSectorCount );/* 0x006 / 0x01C4 */
FF_putLong (pucBuffer, ulPartitionOffset + OFS_PART_STARTING_LBA_32, writeParts[ xPartitionNumber ].ulStartLBA ); /* 0x008 / 0x01C6 This is important */
FF_putLong (pucBuffer, ulPartitionOffset + OFS_PART_LENGTH_32, writeParts[ xPartitionNumber ].ulSectorCount );/* 0x00C / 0x01CA Equal to total sectors */
}
pucBuffer[510] = 0x55;
pucBuffer[511] = 0xAA;
FF_ReleaseBuffer(pxIOManager, pxSectorBuffer );
FF_FlushCache( pxIOManager );
ulLBA = ulNextLBA;
}
}
else
{
pxSectorBuffer = FF_GetBuffer( pxIOManager, 0, ( uint8_t ) FF_MODE_WRITE );
{
if( pxSectorBuffer == NULL )
{
return FF_ERR_DEVICE_DRIVER_FAILED;
}
}
pucBuffer = pxSectorBuffer->pucBuffer;
memset (pucBuffer, 0, 512 );
memcpy (pucBuffer + OFS_BPB_jmpBoot_24, "\xEB\x00\x90" "FreeRTOS", 11 ); /* Includes OFS_BPB_OEMName_64 */
ulPartitionOffset = OFS_PTABLE_PART_0;
for( xPartitionNumber = 0; xPartitionNumber < ffconfigMAX_PARTITIONS; xPartitionNumber++ )
{
FF_putChar( pucBuffer, ulPartitionOffset + OFS_PART_ACTIVE_8, pxPartitions[ xPartitionNumber ].ucActive ); /* 0x01BE 0x80 if active */
FF_putChar( pucBuffer, ulPartitionOffset + OFS_PART_START_HEAD_8, 1 ); /* 0x001 / 0x01BF */
FF_putShort( pucBuffer, ulPartitionOffset + OFS_PART_START_SEC_TRACK_16, 1 ); /* 0x002 / 0x01C0 */
FF_putChar( pucBuffer, ulPartitionOffset + OFS_PART_ID_NUMBER_8, pxPartitions[ xPartitionNumber ].ucPartitionID ); /* 0x004 / 0x01C2 */
FF_putChar( pucBuffer, ulPartitionOffset + OFS_PART_ENDING_HEAD_8, 0xFE ); /* 0x005 / 0x01C3 */
FF_putShort( pucBuffer, ulPartitionOffset + OFS_PART_ENDING_SEC_TRACK_16, pxPartitions[ xPartitionNumber ].ulSectorCount ); /* 0x006 / 0x01C4 */
FF_putLong( pucBuffer, ulPartitionOffset + OFS_PART_STARTING_LBA_32, pxPartitions[ xPartitionNumber ].ulStartLBA ); /* 0x008 / 0x01C6 This is important */
FF_putLong( pucBuffer, ulPartitionOffset + OFS_PART_LENGTH_32, pxPartitions[ xPartitionNumber ].ulSectorCount ); /* 0x00C / 0x01CA Equal to total sectors */
ulPartitionOffset += 16;
}
pucBuffer[ 510 ] = 0x55;
pucBuffer[ 511 ] = 0xAA;
FF_ReleaseBuffer( pxIOManager, pxSectorBuffer );
FF_FlushCache( pxIOManager );
}
return FF_ERR_NONE;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,325 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "ff_headers.h"
#include "event_groups.h"
#ifndef configUSE_RECURSIVE_MUTEXES
#error configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h
#else
#if( configUSE_RECURSIVE_MUTEXES != 1 )
#error configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h
#endif
#endif /* configUSE_RECURSIVE_MUTEXES */
#if ( INCLUDE_vTaskDelay != 1 )
#error Missing some FreeRTOS define
#endif
/* There are two areas which are protected with a semaphore:
Directories and the FAT area.
The masks below are used when calling Group Event functions. */
#define FF_FAT_LOCK_EVENT_BITS ( ( const EventBits_t ) FF_FAT_LOCK )
#define FF_DIR_LOCK_EVENT_BITS ( ( const EventBits_t ) FF_DIR_LOCK )
/* This is not a real lock: it is a bit (or semaphore) will will be given
each time when a sector buffer is released. */
#define FF_BUF_LOCK_EVENT_BITS ( ( const EventBits_t ) FF_BUF_LOCK )
/*-----------------------------------------------------------*/
BaseType_t FF_TrySemaphore( void *pxSemaphore, uint32_t ulTime_ms )
{
BaseType_t xReturn;
/* HT: Actually FF_TrySemaphore is never used. */
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
return 0;
}
configASSERT( pxSemaphore );
xReturn = xSemaphoreTakeRecursive( ( SemaphoreHandle_t ) pxSemaphore, pdMS_TO_TICKS( ulTime_ms ) );
return xReturn;
}
/*-----------------------------------------------------------*/
void FF_PendSemaphore( void *pxSemaphore )
{
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* No need to take the semaphore. */
return;
}
configASSERT( pxSemaphore );
xSemaphoreTakeRecursive( ( SemaphoreHandle_t ) pxSemaphore, portMAX_DELAY );
}
/*-----------------------------------------------------------*/
void FF_ReleaseSemaphore( void *pxSemaphore )
{
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* Scheduler not yet active. */
return;
}
configASSERT( pxSemaphore );
xSemaphoreGiveRecursive( ( SemaphoreHandle_t ) pxSemaphore );
}
/*-----------------------------------------------------------*/
void FF_Sleep( uint32_t ulTime_ms )
{
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* This sleep is used as a kind of yield.
Not necessary while the Scheduler does not run. */
return;
}
vTaskDelay( pdMS_TO_TICKS( ulTime_ms ) );
}
/*-----------------------------------------------------------*/
void FF_DeleteEvents( FF_IOManager_t *pxIOManager )
{
if( pxIOManager->xEventGroup != NULL )
{
vEventGroupDelete( pxIOManager->xEventGroup );
}
}
/*-----------------------------------------------------------*/
BaseType_t FF_CreateEvents( FF_IOManager_t *pxIOManager )
{
BaseType_t xResult;
pxIOManager->xEventGroup = xEventGroupCreate();
if( pxIOManager->xEventGroup != NULL )
{
xEventGroupSetBits( pxIOManager->xEventGroup,
FF_FAT_LOCK_EVENT_BITS | FF_DIR_LOCK_EVENT_BITS | FF_BUF_LOCK_EVENT_BITS );
xResult = pdTRUE;
}
else
{
xResult = pdFALSE;
}
return xResult;
}
/*-----------------------------------------------------------*/
void FF_LockDirectory( FF_IOManager_t *pxIOManager )
{
EventBits_t xBits;
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* Scheduler not yet active. */
return;
}
for( ;; )
{
/* Called when a task want to make changes to a directory.
First it waits for the desired bit to come high. */
xEventGroupWaitBits( pxIOManager->xEventGroup,
FF_DIR_LOCK_EVENT_BITS, /* uxBitsToWaitFor */
( EventBits_t )0, /* xClearOnExit */
pdFALSE, /* xWaitForAllBits n.a. */
pdMS_TO_TICKS( 10000UL ) );
/* The next operation will only succeed for 1 task at a time,
because it is an atomary test & set operation: */
xBits = xEventGroupClearBits( pxIOManager->xEventGroup, FF_DIR_LOCK_EVENT_BITS );
if( ( xBits & FF_DIR_LOCK_EVENT_BITS ) != 0 )
{
/* This task has cleared the desired bit.
It now 'owns' the resource. */
break;
}
}
}
/*-----------------------------------------------------------*/
void FF_UnlockDirectory( FF_IOManager_t *pxIOManager )
{
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* Scheduler not yet active. */
return;
}
configASSERT( ( xEventGroupGetBits( pxIOManager->xEventGroup ) & FF_DIR_LOCK_EVENT_BITS ) == 0 );
xEventGroupSetBits( pxIOManager->xEventGroup, FF_DIR_LOCK_EVENT_BITS );
}
/*-----------------------------------------------------------*/
int FF_Has_Lock( FF_IOManager_t *pxIOManager, uint32_t aBits )
{
int iReturn;
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* Scheduler not yet active. */
return 0;
}
void *handle = xTaskGetCurrentTaskHandle();
if( ( aBits & FF_FAT_LOCK_EVENT_BITS ) != 0 )
{
if( ( pxIOManager->pvFATLockHandle != NULL ) && ( pxIOManager->pvFATLockHandle == handle ) )
{
iReturn = pdTRUE;
}
else
{
iReturn = pdFALSE;
}
}
else
{
iReturn = pdFALSE;
}
return iReturn;
}
void FF_Assert_Lock( FF_IOManager_t *pxIOManager, uint32_t aBits )
{
void *handle;
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* Scheduler not yet active. */
return;
}
handle = xTaskGetCurrentTaskHandle();
if( ( aBits & FF_FAT_LOCK_EVENT_BITS ) != 0 )
{
configASSERT( ( pxIOManager->pvFATLockHandle != NULL ) && ( pxIOManager->pvFATLockHandle == handle ) );
/* In case configASSERT() is not defined. */
( void ) pxIOManager;
( void ) handle;
}
}
void FF_LockFAT( FF_IOManager_t *pxIOManager )
{
EventBits_t xBits;
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* Scheduler not yet active. */
return;
}
configASSERT( FF_Has_Lock( pxIOManager, FF_FAT_LOCK ) == pdFALSE );
for( ;; )
{
/* Called when a task want to make changes to the FAT area.
First it waits for the desired bit to come high. */
xEventGroupWaitBits( pxIOManager->xEventGroup,
FF_FAT_LOCK_EVENT_BITS, /* uxBitsToWaitFor */
( EventBits_t )0, /* xClearOnExit */
pdFALSE, /* xWaitForAllBits n.a. */
pdMS_TO_TICKS( 10000UL ) );
/* The next operation will only succeed for 1 task at a time,
because it is an atomary test & set operation: */
xBits = xEventGroupClearBits( pxIOManager->xEventGroup, FF_FAT_LOCK_EVENT_BITS );
if( ( xBits & FF_FAT_LOCK_EVENT_BITS ) != 0 )
{
/* This task has cleared the desired bit.
It now 'owns' the resource. */
pxIOManager->pvFATLockHandle = xTaskGetCurrentTaskHandle();
break;
}
}
}
/*-----------------------------------------------------------*/
void FF_UnlockFAT( FF_IOManager_t *pxIOManager )
{
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* Scheduler not yet active. */
return;
}
configASSERT( ( xEventGroupGetBits( pxIOManager->xEventGroup ) & FF_FAT_LOCK_EVENT_BITS ) == 0 );
pxIOManager->pvFATLockHandle = NULL;
xEventGroupSetBits( pxIOManager->xEventGroup, FF_FAT_LOCK_EVENT_BITS );
}
/*-----------------------------------------------------------*/
BaseType_t FF_BufferWait( FF_IOManager_t *pxIOManager, uint32_t xWaitMS )
{
EventBits_t xBits;
BaseType_t xReturn;
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* Scheduler not yet active. */
return pdTRUE;
}
/* This function is called when a task is waiting for a sector buffer
to become available. */
xBits = xEventGroupWaitBits( pxIOManager->xEventGroup,
FF_BUF_LOCK_EVENT_BITS, /* uxBitsToWaitFor */
FF_BUF_LOCK_EVENT_BITS, /* xClearOnExit */
pdFALSE, /* xWaitForAllBits n.a. */
pdMS_TO_TICKS( xWaitMS ) );
if( ( xBits & FF_BUF_LOCK_EVENT_BITS ) != 0 )
{
xReturn = pdTRUE;
}
else
{
xReturn = pdFALSE;
}
return xReturn;
}
/*-----------------------------------------------------------*/
void FF_BufferProceed( FF_IOManager_t *pxIOManager )
{
if( xTaskGetSchedulerState() != taskSCHEDULER_RUNNING )
{
/* Scheduler not yet active. */
return;
}
/* Wake-up all tasks that are waiting for a sector buffer to become available. */
xEventGroupSetBits( pxIOManager->xEventGroup, FF_BUF_LOCK_EVENT_BITS );
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,262 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "ff_headers.h"
#include "event_groups.h"
#ifndef configUSE_RECURSIVE_MUTEXES
#error configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h
#else
#if( configUSE_RECURSIVE_MUTEXES != 1 )
#error configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h
#endif
#endif /* configUSE_RECURSIVE_MUTEXES */
#if ( INCLUDE_vTaskDelay != 1 )
#error Missing some FreeRTOS define
#endif
/* There are two areas which are protected with a semaphore:
Directories and the FAT area.
The masks below are used when calling Group Event functions. */
#define FF_FAT_LOCK_EVENT_BITS ( ( const EventBits_t ) FF_FAT_LOCK )
#define FF_DIR_LOCK_EVENT_BITS ( ( const EventBits_t ) FF_DIR_LOCK )
/* This is not a real lock: it is a bit (or semaphore) will will be given
each time when a sector buffer is released. */
#define FF_BUF_LOCK_EVENT_BITS ( ( const EventBits_t ) FF_BUF_LOCK )
/*-----------------------------------------------------------*/
BaseType_t FF_TrySemaphore( void *pxSemaphore, uint32_t ulTime_ms )
{
BaseType_t xReturn;
configASSERT( pxSemaphore );
xReturn = xSemaphoreTakeRecursive( ( SemaphoreHandle_t ) pxSemaphore, pdMS_TO_TICKS( ulTime_ms ) );
return xReturn;
}
/*-----------------------------------------------------------*/
void FF_PendSemaphore( void *pxSemaphore )
{
configASSERT( pxSemaphore );
xSemaphoreTakeRecursive( ( SemaphoreHandle_t ) pxSemaphore, portMAX_DELAY );
}
/*-----------------------------------------------------------*/
void FF_ReleaseSemaphore( void *pxSemaphore )
{
configASSERT( pxSemaphore );
xSemaphoreGiveRecursive( ( SemaphoreHandle_t ) pxSemaphore );
}
/*-----------------------------------------------------------*/
void FF_Sleep( uint32_t ulTime_ms )
{
vTaskDelay( pdMS_TO_TICKS( ulTime_ms ) );
}
/*-----------------------------------------------------------*/
void FF_DeleteEvents( FF_IOManager_t *pxIOManager )
{
if( pxIOManager->xEventGroup != NULL )
{
vEventGroupDelete( pxIOManager->xEventGroup );
}
}
/*-----------------------------------------------------------*/
BaseType_t FF_CreateEvents( FF_IOManager_t *pxIOManager )
{
BaseType_t xResult;
pxIOManager->xEventGroup = xEventGroupCreate();
if( pxIOManager->xEventGroup != NULL )
{
xEventGroupSetBits( pxIOManager->xEventGroup,
FF_FAT_LOCK_EVENT_BITS | FF_DIR_LOCK_EVENT_BITS | FF_BUF_LOCK_EVENT_BITS );
xResult = pdTRUE;
}
else
{
xResult = pdFALSE;
}
return xResult;
}
/*-----------------------------------------------------------*/
void FF_LockDirectory( FF_IOManager_t *pxIOManager )
{
EventBits_t xBits;
for( ;; )
{
/* Called when a task want to make changes to a directory.
First it waits for the desired bit to come high. */
xEventGroupWaitBits( pxIOManager->xEventGroup,
FF_DIR_LOCK_EVENT_BITS, /* uxBitsToWaitFor */
( EventBits_t )0, /* xClearOnExit */
pdFALSE, /* xWaitForAllBits n.a. */
pdMS_TO_TICKS( 10000UL ) );
/* The next operation will only succeed for 1 task at a time,
because it is an atomary test & set operation: */
xBits = xEventGroupClearBits( pxIOManager->xEventGroup, FF_DIR_LOCK_EVENT_BITS );
if( ( xBits & FF_DIR_LOCK_EVENT_BITS ) != 0 )
{
/* This task has cleared the desired bit.
It now 'owns' the resource. */
break;
}
}
}
/*-----------------------------------------------------------*/
void FF_UnlockDirectory( FF_IOManager_t *pxIOManager )
{
configASSERT( ( xEventGroupGetBits( pxIOManager->xEventGroup ) & FF_DIR_LOCK_EVENT_BITS ) == 0 );
xEventGroupSetBits( pxIOManager->xEventGroup, FF_DIR_LOCK_EVENT_BITS );
}
/*-----------------------------------------------------------*/
int FF_Has_Lock( FF_IOManager_t *pxIOManager, uint32_t aBits )
{
int iReturn;
void *handle = xTaskGetCurrentTaskHandle();
if( ( aBits & FF_FAT_LOCK_EVENT_BITS ) != 0 )
{
if( ( pxIOManager->pvFATLockHandle != NULL ) && ( pxIOManager->pvFATLockHandle == handle ) )
{
iReturn = pdTRUE;
}
else
{
iReturn = pdFALSE;
}
}
else
{
iReturn = pdFALSE;
}
return iReturn;
}
void FF_Assert_Lock( FF_IOManager_t *pxIOManager, uint32_t aBits )
{
void *handle = xTaskGetCurrentTaskHandle();
if( ( aBits & FF_FAT_LOCK_EVENT_BITS ) != 0 )
{
configASSERT( ( pxIOManager->pvFATLockHandle != NULL ) && ( pxIOManager->pvFATLockHandle == handle ) );
/* In case configASSERT() is not defined. */
( void ) pxIOManager;
( void ) handle;
}
}
void FF_LockFAT( FF_IOManager_t *pxIOManager )
{
EventBits_t xBits;
configASSERT( FF_Has_Lock( pxIOManager, FF_FAT_LOCK ) == pdFALSE );
for( ;; )
{
/* Called when a task want to make changes to the FAT area.
First it waits for the desired bit to come high. */
xEventGroupWaitBits( pxIOManager->xEventGroup,
FF_FAT_LOCK_EVENT_BITS, /* uxBitsToWaitFor */
( EventBits_t )0, /* xClearOnExit */
pdFALSE, /* xWaitForAllBits n.a. */
pdMS_TO_TICKS( 10000UL ) );
/* The next operation will only succeed for 1 task at a time,
because it is an atomary test & set operation: */
xBits = xEventGroupClearBits( pxIOManager->xEventGroup, FF_FAT_LOCK_EVENT_BITS );
if( ( xBits & FF_FAT_LOCK_EVENT_BITS ) != 0 )
{
/* This task has cleared the desired bit.
It now 'owns' the resource. */
pxIOManager->pvFATLockHandle = xTaskGetCurrentTaskHandle();
break;
}
}
}
/*-----------------------------------------------------------*/
void FF_UnlockFAT( FF_IOManager_t *pxIOManager )
{
configASSERT( ( xEventGroupGetBits( pxIOManager->xEventGroup ) & FF_FAT_LOCK_EVENT_BITS ) == 0 );
pxIOManager->pvFATLockHandle = NULL;
xEventGroupSetBits( pxIOManager->xEventGroup, FF_FAT_LOCK_EVENT_BITS );
}
/*-----------------------------------------------------------*/
BaseType_t FF_BufferWait( FF_IOManager_t *pxIOManager, uint32_t xWaitMS )
{
EventBits_t xBits;
BaseType_t xReturn;
/* This function is called when a task is waiting for a sector buffer
to become available. */
xBits = xEventGroupWaitBits( pxIOManager->xEventGroup,
FF_BUF_LOCK_EVENT_BITS, /* uxBitsToWaitFor */
FF_BUF_LOCK_EVENT_BITS, /* xClearOnExit */
pdFALSE, /* xWaitForAllBits n.a. */
pdMS_TO_TICKS( xWaitMS ) );
if( ( xBits & FF_BUF_LOCK_EVENT_BITS ) != 0 )
{
xReturn = pdTRUE;
}
else
{
xReturn = pdFALSE;
}
return xReturn;
}
/*-----------------------------------------------------------*/
void FF_BufferProceed( FF_IOManager_t *pxIOManager )
{
/* Wake-up all tasks that are waiting for a sector buffer to become available. */
xEventGroupSetBits( pxIOManager->xEventGroup, FF_BUF_LOCK_EVENT_BITS );
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,108 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
/**
* @file ff_memory.c
* @ingroup MEMORY
*
* @defgroup MEMORY FreeRTOS+FAT Memory Access Routines
* @brief Handles memory access in a portable way.
*
* Provides simple, fast, and portable access to memory routines.
* These are only used to read data from buffers. That are LITTLE ENDIAN
* due to the FAT specification.
*
* These routines may need to be modified to your platform.
*
**/
#include "ff_headers.h"
/*
* Here below 3 x 2 access functions that allow the code
* not to worry about the endianness of the MCU.
*/
#if( ffconfigINLINE_MEMORY_ACCESS == 0 )
uint8_t FF_getChar( const uint8_t *pBuffer, uint32_t aOffset )
{
return ( uint8_t ) ( pBuffer[ aOffset ] );
}
uint16_t FF_getShort( const uint8_t *pBuffer, uint32_t aOffset )
{
FF_T_UN16 u16;
pBuffer += aOffset;
u16.bytes.u8_1 = pBuffer[ 1 ];
u16.bytes.u8_0 = pBuffer[ 0 ];
return u16.u16;
}
uint32_t FF_getLong( const uint8_t *pBuffer, uint32_t aOffset )
{
FF_T_UN32 u32;
pBuffer += aOffset;
u32.bytes.u8_3 = pBuffer[ 3 ];
u32.bytes.u8_2 = pBuffer[ 2 ];
u32.bytes.u8_1 = pBuffer[ 1 ];
u32.bytes.u8_0 = pBuffer[ 0 ];
return u32.u32;
}
void FF_putChar( uint8_t *pBuffer, uint32_t aOffset, uint32_t Value )
{
pBuffer[ aOffset ] = ( uint8_t ) Value;
}
void FF_putShort( uint8_t *pBuffer, uint32_t aOffset, uint32_t Value )
{
FF_T_UN16 u16;
u16.u16 = ( uint16_t ) Value;
pBuffer += aOffset;
pBuffer[ 0 ] = u16.bytes.u8_0;
pBuffer[ 1 ] = u16.bytes.u8_1;
}
void FF_putLong( uint8_t *pBuffer, uint32_t aOffset, uint32_t Value )
{
FF_T_UN32 u32;
u32.u32 = Value;
pBuffer += aOffset;
pBuffer[ 0 ] = u32.bytes.u8_0;
pBuffer[ 1 ] = u32.bytes.u8_1;
pBuffer[ 2 ] = u32.bytes.u8_2;
pBuffer[ 3 ] = u32.bytes.u8_3;
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,716 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
/**
* @file ff_string.c
* @ingroup STRING
*
* @defgroup STRING FreeRTOS+FAT String Library
* @brief Portable String Library for FreeRTOS+FAT
*
*
**/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "ff_headers.h"
#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
#include <wchar.h>
#include <wctype.h>
#endif
/*
* These will eventually be moved into a platform independent string
* library. Which will be optional. (To allow the use of system specific versions).
*/
#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
void FF_cstrntowcs( FF_T_WCHAR *wcsDest, const int8_t *szpSource, uint32_t ulLength )
{
while( ( *szpSource != '\0' ) && ( ulLength-- != 0 ) )
{
*( wcsDest++ ) = *( szpSource++ );
}
*wcsDest = '\0';
}
#endif /* ffconfigUNICODE_UTF16_SUPPORT */
/*-----------------------------------------------------------*/
#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
void FF_cstrtowcs( FF_T_WCHAR *wcsDest, const int8_t *szpSource )
{
while( *szpSource != '\0' )
{
*wcsDest++ = ( FF_T_WCHAR ) *( szpSource++ );
}
*wcsDest = '\0';
}
#endif /* ffconfigUNICODE_UTF16_SUPPORT */
/*-----------------------------------------------------------*/
#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
void FF_wcstocstr( int8_t *szpDest, const FF_T_WCHAR *wcsSource )
{
while( *wcsSource != '\0' )
{
*szpDest++ = ( int8_t )*( wcsSource++ );
}
*szpDest = '\0';
}
#endif /* ffconfigUNICODE_UTF16_SUPPORT */
/*-----------------------------------------------------------*/
#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
void FF_wcsntocstr( int8_t *szpDest, const FF_T_WCHAR *wcsSource, uint32_t ulLength )
{
while( ( *wcsSource != '\0' ) && ( ulLength-- != 0 ) )
{
*( szpDest++ ) = ( int8_t ) *( wcsSource++ );
}
*szpDest = '\0';
}
#endif /* ffconfigUNICODE_UTF16_SUPPORT */
/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/
#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
void FF_toupper( FF_T_WCHAR *string, uint32_t ulLength )
{
uint32_t i;
for( i = 0; i < ulLength; i++ )
{
string[ i ] = towupper( string[ i ] );
}
}
/*-----------------------------------------------------------*/
void FF_tolower( FF_T_WCHAR *string, uint32_t ulLength )
{
uint32_t i;
for( i = 0; i < ulLength; i++ )
{
string[ i ] = towlower( string[ i ] );
}
}
/*-----------------------------------------------------------*/
#else /* ffconfigUNICODE_UTF16_SUPPORT */
void FF_toupper( char *string, uint32_t ulLength )
{
uint32_t i;
for( i = 0; i < ulLength; i++ )
{
if( ( string[ i ] >= 'a' ) && ( string[ i ] <= 'z' ) )
{
string[ i ] -= 32;
}
if( string[ i ] == '\0' )
{
break;
}
}
}
/*-----------------------------------------------------------*/
void FF_tolower( char *string, uint32_t ulLength )
{
uint32_t i;
for( i = 0; i < ulLength; i++ )
{
if( ( string[ i ] >= 'A' ) && ( string[ i ] <= 'Z' ) )
{
string[ i ] += 32;
}
if( string[ i ] == '\0' )
{
break;
}
}
}
/*-----------------------------------------------------------*/
#endif /* ffconfigUNICODE_UTF16_SUPPORT */
/**
* @private
* @brief Compares 2 strings for the specified length, and returns pdTRUE is they are identical
* otherwise pdFALSE is returned.
*
**/
#if( ffconfigUNICODE_UTF16_SUPPORT == 0 )
BaseType_t FF_strmatch( const char *str1, const char *str2, BaseType_t xLength )
{
register BaseType_t i;
register char char1, char2;
if( xLength == 0 )
{
xLength = strlen( str1 );
if( xLength != ( BaseType_t )strlen( str2 ) )
{
return pdFALSE;
}
}
for( i = 0; i < xLength; i++ )
{
char1 = str1[ i ];
char2 = str2[ i ];
if( ( char1 >= 'A' ) && ( char1 <= 'Z' ) )
{
char1 += 32;
}
if( ( char2 >= 'A' ) && ( char2 <= 'Z' ) )
{
char2 += 32;
}
if( char1 != char2 )
{
return pdFALSE;
}
}
return pdTRUE;
}
#else /* ffconfigUNICODE_UTF16_SUPPORT */
BaseType_t FF_strmatch( const FF_T_WCHAR *str1, const FF_T_WCHAR *str2, BaseType_t xLength )
{
register BaseType_t i;
register FF_T_WCHAR char1, char2;
if( xLength == 0 )
{
xLength = wcslen( str1 );
if( xLength != wcslen( str2 ) )
{
return pdFALSE;
}
}
for( i = 0; i < xLength; i++ )
{
char1 = towlower( str1[ i ] );
char2 = towlower( str2[ i ] );
if( char1 != char2 )
{
return pdFALSE;
}
}
return pdTRUE;
}
#endif /* ffconfigUNICODE_UTF16_SUPPORT */
/**
* @private
* @brief A re-entrant Strtok function. No documentation is provided :P
* Use at your own risk. (This is for FreeRTOS+FAT's use only).
**/
#if( ffconfigUNICODE_UTF16_SUPPORT == 0 )
char *FF_strtok( const char *string, char *token, uint16_t *tokenNumber, BaseType_t *last, BaseType_t xLength )
{
uint16_t i,y, tokenStart, tokenEnd = 0;
i = 0;
y = 0;
if( ( string[ i ] == '\\' ) || ( string[ i ] == '/' ) )
{
i++;
}
tokenStart = i;
while( i < xLength )
{
if( ( string[ i ] == '\\' ) || ( string[ i ] == '/' ) )
{
y++;
if( y == *tokenNumber )
{
tokenStart = ( uint16_t )( i + 1 );
}
if( y == ( *tokenNumber + 1 ) )
{
tokenEnd = i;
break;
}
}
i++;
}
if( tokenEnd == 0 )
{
if( *last == pdTRUE )
{
return NULL;
}
else
{
*last = pdTRUE;
}
tokenEnd = i;
}
if( ( tokenEnd - tokenStart ) < ffconfigMAX_FILENAME )
{
memcpy( token, ( string + tokenStart ), ( uint32_t )( tokenEnd - tokenStart ) );
token[ tokenEnd - tokenStart ] = '\0';
}
else
{
memcpy( token, ( string + tokenStart ), ( uint32_t )( ffconfigMAX_FILENAME ) );
token[ ffconfigMAX_FILENAME - 1 ] = '\0';
}
/*token[tokenEnd - tokenStart] = '\0'; */
*tokenNumber += 1;
return token;
}
#else /* ffconfigUNICODE_UTF16_SUPPORT */
FF_T_WCHAR *FF_strtok( const FF_T_WCHAR *string, FF_T_WCHAR *token, uint16_t *tokenNumber, BaseType_t *last, BaseType_t xLength )
{
uint16_t i,y, tokenStart, tokenEnd = 0;
i = 0;
y = 0;
if( ( string[ i ] == '\\' ) || ( string[ i ] == '/' ) )
{
i++;
}
tokenStart = i;
while( i < xLength )
{
if( ( string[ i ] == '\\' ) || ( string[ i ] == '/' ) )
{
y++;
if( y == *tokenNumber )
{
tokenStart = ( uint16_t ) ( i + 1 );
}
if( y == ( *tokenNumber + 1 ) )
{
tokenEnd = i;
break;
}
}
i++;
}
if( tokenEnd == 0 )
{
if( *last == pdTRUE )
{
return NULL;
}
else
{
*last = pdTRUE;
}
tokenEnd = i;
}
if( ( tokenEnd - tokenStart ) < ffconfigMAX_FILENAME )
{
memcpy( token, ( string + tokenStart ), ( uint32_t )( tokenEnd - tokenStart ) * sizeof( FF_T_WCHAR ) );
token[ tokenEnd - tokenStart ] = '\0';
}
else
{
memcpy( token, ( string + tokenStart ), ( uint32_t )( ffconfigMAX_FILENAME ) * sizeof( FF_T_WCHAR ) );
token[ ffconfigMAX_FILENAME - 1 ] = '\0';
}
/*token[tokenEnd - tokenStart] = '\0'; */
*tokenNumber += 1;
return token;
}
#endif /* ffconfigUNICODE_UTF16_SUPPORT */
/* UTF-8 Routines */
/*
UCS-4 range (hex.) UTF-8 octet sequence (binary)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -- We don't encode these because we won't receive them. (Invalid UNICODE).
0400 0000-7FFF FFFF 1111110x 10xxxxxx ... 10xxxxxx -- We don't encode these because we won't receive them. (Invalid UNICODE).
*/
#if ( ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) && ( WCHAR_MAX > 0xFFFF ) ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
UBaseType_t FF_GetUtf16SequenceLen( uint16_t usLeadChar )
{
UBaseType_t uxReturn;
if( ( usLeadChar & 0xFC00 ) == 0xD800 )
{
uxReturn = 2;
}
else
{
uxReturn = 1;
}
return uxReturn;
} /* FF_GetUtf16SequenceLen() */
#endif
/*-----------------------------------------------------------*/
/*
Returns the number of UTF-8 units read.
Will not exceed ulSize UTF-16 units. (ulSize * 2 bytes).
*/
/*
UCS-4 range (hex.) UTF-8 octet sequence (binary)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -- We don't encode these because we won't receive them. (Invalid UNICODE).
0400 0000-7FFF FFFF 1111110x 10xxxxxx ... 10xxxxxx -- We don't encode these because we won't receive them. (Invalid UNICODE).
*/
#if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
int32_t FF_Utf8ctoUtf16c( uint16_t *utf16Dest, const uint8_t *utf8Source, uint32_t ulSize )
{
uint32_t ulUtf32char;
uint16_t utf16Source = 0;
register int32_t lSequenceNumber = 0;
/* Count number of set bits before a zero. */
while( ( ( *utf8Source != '\0' ) & ( 0x80 >> ( lSequenceNumber ) ) ) )
{
lSequenceNumber++;
}
if( lSequenceNumber == 0UL )
{
lSequenceNumber++;
}
if( ulSize == 0UL )
{
/* Returned value becomes an error, with the highest bit set. */
lSequenceNumber = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF8CTOUTF16C;
}
else
{
switch( lSequenceNumber )
{
case 1:
utf16Source = (uint16_t) *utf8Source;
memcpy(utf16Dest,&utf16Source,sizeof(uint16_t));
break;
case 2:
utf16Source =(uint16_t) ((*utf8Source & 0x1F) << 6) | ((*(utf8Source + 1) & 0x3F));
memcpy(utf16Dest,&utf16Source,sizeof(uint16_t));
break;
case 3:
utf16Source =(uint16_t) ((*utf8Source & 0x0F) << 12) | ((*(utf8Source + 1) & 0x3F) << 6) | ((*(utf8Source + 2) & 0x3F));
memcpy(utf16Dest,&utf16Source,sizeof(uint16_t));
break;
case 4:
if( ulSize < 2 )
{
/* Returned value becomes an error. */
lSequenceNumber = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF8CTOUTF16C;
}
else
{
/* Convert to UTF-32 and then into UTF-16 */
ulUtf32char = ( uint16_t )
( ( *utf8Source & 0x0F ) << 18 ) |
( ( *( utf8Source + 1 ) & 0x3F ) << 12 ) |
( ( *( utf8Source + 2 ) & 0x3F ) << 6 ) |
( ( *( utf8Source + 3 ) & 0x3F ) );
utf16Source = ( uint16_t ) ( ( ( ulUtf32char - 0x10000 ) & 0xFFC00 ) >> 10 ) | 0xD800;
memcpy( utf16Dest, &utf16Source, sizeof( uint16_t ) );
utf16Source = ( uint16_t ) ( ( ( ulUtf32char - 0x10000 ) & 0x003FF ) >> 00 ) | 0xDC00;
memcpy( utf16Dest + 1, &utf16Source, sizeof( uint16_t ) );
}
break;
default:
break;
}
}
return lSequenceNumber;
} /* FF_Utf8ctoUtf16c() */
#endif /* ffconfigUNICODE_UTF8_SUPPORT */
/*-----------------------------------------------------------*/
/*
Returns the number of UTF-8 units required to encode the UTF-16 sequence.
Will not exceed ulSize UTF-8 units. (ulSize * 1 bytes).
*/
#if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
int32_t FF_Utf16ctoUtf8c( uint8_t *utf8Dest, const uint16_t *utf16Source, uint32_t ulSize )
{
uint32_t ulUtf32char;
uint16_t ulUtf16char;
int32_t lReturn = 0L;
do
{
if( ulSize == 0UL )
{
lReturn = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF16CTOUTF8C;
break;
}
memcpy( &ulUtf16char, utf16Source, sizeof( uint16_t ) );
/* A surrogate sequence was encountered. Must transform to UTF32 first. */
if( ( ulUtf16char & 0xF800) == 0xD800 )
{
ulUtf32char = ( ( uint32_t ) ( ulUtf16char & 0x003FF ) << 10 ) + 0x10000;
memcpy( &ulUtf16char, utf16Source + 1, sizeof( uint16_t ) );
if( ( ulUtf16char & 0xFC00 ) != 0xDC00 )
{
/* Invalid UTF-16 sequence. */
lReturn = FF_ERR_UNICODE_INVALID_SEQUENCE | FF_UTF16CTOUTF8C;
break;
}
ulUtf32char |= ( ( uint32_t ) ( ulUtf16char & 0x003FF ) );
}
else
{
ulUtf32char = ( uint32_t ) ulUtf16char;
}
/* Now convert to the UTF-8 sequence. */
/* Single byte UTF-8 sequence. */
if( ulUtf32char < 0x00000080 )
{
*( utf8Dest + 0 ) = ( uint8_t )ulUtf32char;
lReturn = 1;
break;
}
/* Double byte UTF-8 sequence. */
if( ulUtf32char < 0x00000800 )
{
if( ulSize < 2 )
{
lReturn = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF16CTOUTF8C;
}
else
{
*( utf8Dest + 0 ) = ( uint8_t ) ( 0xC0 | ( ( ulUtf32char >> 6 ) & 0x1F ) );
*( utf8Dest + 1 ) = ( uint8_t ) ( 0x80 | ( ( ulUtf32char >> 0 ) & 0x3F ) );
lReturn = 2;
}
break;
}
/* Triple byte UTF-8 sequence. */
if( ulUtf32char < 0x00010000 )
{
if( ulSize < 3 )
{
lReturn = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF16CTOUTF8C;
}
else
{
*( utf8Dest + 0 ) = ( uint8_t ) ( 0xE0 | ( ( ulUtf32char >> 12 ) & 0x0F ) );
*( utf8Dest + 1 ) = ( uint8_t ) ( 0x80 | ( ( ulUtf32char >> 6 ) & 0x3F ) );
*( utf8Dest + 2 ) = ( uint8_t ) ( 0x80 | ( ( ulUtf32char >> 0 ) & 0x3F ) );
lReturn = 3;
}
break;
}
/* Quadruple byte UTF-8 sequence. */
if( ulUtf32char < 0x00200000 )
{
if( ulSize < 4 )
{
lReturn = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF16CTOUTF8C;
}
else
{
*( utf8Dest + 0 ) = ( uint8_t ) (0xF0 | ( ( ulUtf32char >> 18 ) & 0x07 ) );
*( utf8Dest + 1 ) = ( uint8_t ) (0x80 | ( ( ulUtf32char >> 12 ) & 0x3F ) );
*( utf8Dest + 2 ) = ( uint8_t ) (0x80 | ( ( ulUtf32char >> 6 ) & 0x3F ) );
*( utf8Dest + 3 ) = ( uint8_t ) (0x80 | ( ( ulUtf32char >> 0 ) & 0x3F ) );
lReturn = 4;
}
break;
}
lReturn = FF_ERR_UNICODE_INVALID_CODE | FF_UTF16CTOUTF8C; /* Invalid Character */
}
while( pdFALSE );
return lReturn;
} /* FF_Utf16ctoUtf8c() */
#endif /* ffconfigUNICODE_UTF8_SUPPORT */
/*-----------------------------------------------------------*/
/* UTF-16 Support Functions */
/* Converts a UTF-32 Character into its equivalent UTF-16 sequence. */
#if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) && ( WCHAR_MAX > 0xFFFF )
int32_t FF_Utf32ctoUtf16c( uint16_t *utf16Dest, uint32_t utf32char, uint32_t ulSize )
{
int32_t lReturn;
/* Check that its a valid UTF-32 wide-char! */
/* This range is not a valid Unicode code point. */
if( ( utf32char >= 0xD800 ) && ( utf32char <= 0xDFFF ) )
{
lReturn = FF_ERR_UNICODE_INVALID_CODE | FF_UTF32CTOUTF16C; /* Invalid character. */
}
else if( utf32char < 0x10000 )
{
*utf16Dest = (uint16_t) utf32char; /* Simple conversion! Char comes within UTF-16 space (without surrogates). */
lReturn = 1;
}
else if( ulSize < 2 )
{
lReturn FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF32CTOUTF16C; /* Not enough UTF-16 units to record this character. */
}
else if( utf32char < 0x00200000 )
{
/* Conversion to a UTF-16 Surrogate pair! */
/*valueImage = utf32char - 0x10000; */
*( utf16Dest + 0 ) = ( uint16_t ) ( ( ( utf32char - 0x10000 ) & 0xFFC00 ) >> 10 ) | 0xD800;
*( utf16Dest + 1 ) = ( uint16_t ) ( ( ( utf32char - 0x10000 ) & 0x003FF ) >> 00 ) | 0xDC00;
lReturn = 2; /* Surrogate pair encoded value. */
}
else
{
/* Invalid Character */
lReturn = FF_ERR_UNICODE_INVALID_CODE | FF_UTF32CTOUTF16C;
}
return lReturn;
} /* FF_Utf32ctoUtf16c() */
#endif /* #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) && ( WCHAR_MAX > 0xFFFF ) */
/*-----------------------------------------------------------*/
/* Converts a UTF-16 sequence into its equivalent UTF-32 code point. */
#if( ffconfigNOT_USED_FOR_NOW != 0 )
int32_t FF_Utf16ctoUtf32c( uint32_t *utf32Dest, const uint16_t *utf16Source )
{
int32_t lReturn;
/*Not a surrogate sequence. */
if( ( *utf16Source & 0xFC00 ) != 0xD800 )
{
*utf32Dest = ( uint32_t )*utf16Source;
lReturn = 1; /* A single UTF-16 item was used to represent the character. */
}
else
{
*utf32Dest = ( ( uint32_t) ( * ( utf16Source + 0 ) & 0x003FF ) << 10 ) + 0x10000;
if( ( *(utf16Source + 1) & 0xFC00 ) != 0xDC00 )
{
lReturn = FF_ERR_UNICODE_INVALID_SEQUENCE | FF_UTF16CTOUTF32C; /* Invalid UTF-16 sequence. */
}
else
{
*utf32Dest |= ( ( uint32_t ) ( *( utf16Source + 1 ) & 0x003FF ) );
lReturn = 2; /* 2 utf-16 units make up the Unicode code-point. */
}
}
return lReturn;
} /* FF_Utf16ctoUtf32c() */
#endif /* ffconfigNOT_USED_FOR_NOW */
/*-----------------------------------------------------------*/
/*
Returns the total number of UTF-16 items required to represent
the provided UTF-32 string in UTF-16 form.
*/
/*
UBaseType_t FF_Utf32GetUtf16Len( const uint32_t *utf32String )
{
UBaseType_t utf16len = 0;
while( *utf32String )
{
if( *utf32String++ <= 0xFFFF )
{
utf16len++;
}
else
{
utf16len += 2;
}
}
return utf16len;
}
*/
/*-----------------------------------------------------------*/
/* String conversions */
#if( ffconfigNOT_USED_FOR_NOW != 0 )
int32_t FF_Utf32stoUtf8s( uint8_t *Utf8String, uint32_t *Utf32String )
{
int i = 0,y = 0;
uint16_t utf16buffer[ 2 ];
while( Utf32String[ i ] != '\0' )
{
/* Convert to a UTF16 char. */
FF_Utf32ctoUtf16c( utf16buffer, Utf32String[ i ], 2 );
/* Now convert the UTF16 to UTF8 sequence. */
y += FF_Utf16ctoUtf8c( &Utf8String[ y ], utf16buffer, 4 );
i++;
}
Utf8String[ y ] = '\0';
return 0;
} /* FF_Utf32stoUtf8s() */
#endif /* ffconfigNOT_USED_FOR_NOW */
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,274 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
#include <stdio.h>
#include <string.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "portable.h"
#include "ff_headers.h"
#include "ff_sys.h"
#ifndef ARRAY_SIZE
# define ARRAY_SIZE(x) ( int )( sizeof( x ) / sizeof( x )[ 0 ] )
#endif
/*
* Define a collection of 'file systems' as a simple array
*/
typedef struct xSYSTEM
{
FF_SubSystem_t xSystems[ ffconfigMAX_FILE_SYS ];
volatile BaseType_t xFileSystemCount;
} ff_sys_t;
static ff_sys_t file_systems;
static const char rootDir[] = "/";
int FF_FS_Count( void )
{
return ( int )file_systems.xFileSystemCount;
}
/*-----------------------------------------------------------*/
void FF_FS_Init( void )
{
memset( &file_systems, '\0', sizeof( file_systems ) );
/* There is always a root file system, even if it doesn't have a
IO manager. */
file_systems.xFileSystemCount = ( BaseType_t ) 1;
/* Set to "/", second byte is already zero. */
file_systems.xSystems[ 0 ].pcPath[ 0 ] = ( char ) '/';
file_systems.xSystems[ 0 ].xPathlen = 1;
}
/*-----------------------------------------------------------*/
int FF_FS_Add( const char *pcPath, FF_Disk_t *pxDisk )
{
int iReturn = pdFALSE;
configASSERT( pxDisk );
if( *pcPath != ( char ) '/' )
{
FF_PRINTF( "FF_FS_Add: Need a \"/\": '%s'\n", pcPath );
}
else
{
BaseType_t xUseIndex = -1;
size_t uxPathLength = strlen( pcPath );
vTaskSuspendAll();
{
if( file_systems.xFileSystemCount == ( BaseType_t ) 0 )
{
FF_FS_Init();
}
if( uxPathLength == ( size_t ) 1u )
{
/* This is the "/" path
* and will always be put at index 0 */
xUseIndex = ( BaseType_t ) 0;
}
else
{
BaseType_t xIndex, xFreeIndex = -1;
FF_SubSystem_t *pxSubSystem = file_systems.xSystems + 1; /* Skip the root entry */
for( xIndex = ( BaseType_t ) 1; xIndex < file_systems.xFileSystemCount; xIndex++, pxSubSystem++ )
{
if( ( pxSubSystem->xPathlen == ( BaseType_t )uxPathLength ) &&
( memcmp( pxSubSystem->pcPath, pcPath, uxPathLength ) == 0 ) )
{
/* A system is updated with a new handler. */
xUseIndex = xIndex;
break;
}
if( ( pxSubSystem->pxManager == NULL ) && ( xFreeIndex < 0 ) )
{
/* Remember the first free slot. */
xFreeIndex = xIndex;
}
}
if( xUseIndex < ( BaseType_t ) 0 )
{
if( xFreeIndex >= ( BaseType_t ) 0 )
{
/* Use the first free slot. */
xUseIndex = xFreeIndex;
}
else if( file_systems.xFileSystemCount < ARRAY_SIZE( file_systems.xSystems ) )
{
/* Fill a new entry. */
xUseIndex = file_systems.xFileSystemCount++;
}
}
} /* uxPathLength != 1 */
if( xUseIndex >= ( BaseType_t ) 0 )
{
iReturn = pdTRUE;
strncpy( file_systems.xSystems[ xUseIndex ].pcPath, pcPath, sizeof( file_systems.xSystems[ xUseIndex ].pcPath ) );
file_systems.xSystems[ xUseIndex ].xPathlen = uxPathLength;
file_systems.xSystems[ xUseIndex ].pxManager = pxDisk->pxIOManager;
}
}
xTaskResumeAll( );
if( iReturn == pdFALSE )
{
FF_PRINTF( "FF_FS_Add: Table full '%s' (max = %d)\n", pcPath, (int)ARRAY_SIZE( file_systems.xSystems ) );
}
}
return iReturn;
}
/*-----------------------------------------------------------*/
void FF_FS_Remove( const char *pcPath )
{
BaseType_t xUseIndex, xIndex;
size_t uxPathLength;
if( pcPath[ 0 ] == ( char ) '/' )
{
xUseIndex = -1;
uxPathLength = strlen( pcPath );
/* Is it the "/" path ? */
if( uxPathLength == ( size_t ) 1u )
{
xUseIndex = 0;
}
else
{
FF_SubSystem_t *pxSubSystem = file_systems.xSystems + 1;
for( xIndex = 1; xIndex < file_systems.xFileSystemCount; xIndex++, pxSubSystem++ )
{
if( ( pxSubSystem->xPathlen == ( BaseType_t ) uxPathLength ) &&
( memcmp( pxSubSystem->pcPath, pcPath, uxPathLength ) == 0 ) )
{
xUseIndex = xIndex;
break;
}
}
}
if( xUseIndex >= 0 )
{
vTaskSuspendAll();
{
file_systems.xSystems[ xUseIndex ].pxManager = NULL;
file_systems.xSystems[ xUseIndex ].xPathlen = ( BaseType_t )0;
for( xIndex = file_systems.xFileSystemCount - 1; xIndex > 0; xIndex-- )
{
if( file_systems.xSystems[ xIndex ].pxManager != NULL )
{
/* The slot at 'xIndex' is still in use. */
break;
}
}
file_systems.xFileSystemCount = xIndex + 1;
}
xTaskResumeAll( );
}
}
}
/*-----------------------------------------------------------*/
int FF_FS_Find( const char *pcPath, FF_DirHandler_t *pxHandler )
{
FF_SubSystem_t *pxSubSystem;
size_t uxPathLength;
BaseType_t xUseIndex;
int iReturn;
pxSubSystem = file_systems.xSystems + 1;
uxPathLength = strlen( pcPath );
memset( pxHandler, '\0', sizeof( *pxHandler ) );
for( xUseIndex = 1; xUseIndex < file_systems.xFileSystemCount; xUseIndex++, pxSubSystem++ )
{
if( ( uxPathLength >= ( size_t ) pxSubSystem->xPathlen ) &&
( memcmp( pxSubSystem->pcPath, pcPath, ( size_t ) pxSubSystem->xPathlen ) == 0 ) &&
( ( pcPath[ pxSubSystem->xPathlen ] == '\0' ) || ( pcPath[ pxSubSystem->xPathlen ] == '/') ) ) /* System "/ram" should not match with "/ramc/etc". */
{
if( pcPath[ pxSubSystem->xPathlen ] == '\0')
{
pxHandler->pcPath = rootDir;
}
else
{
pxHandler->pcPath = pcPath + pxSubSystem->xPathlen;
}
pxHandler->pxManager = pxSubSystem->pxManager;
break;
}
}
if( xUseIndex == file_systems.xFileSystemCount )
{
pxHandler->pcPath = pcPath;
pxHandler->pxManager = file_systems.xSystems[ 0 ].pxManager;
}
if( FF_Mounted( pxHandler->pxManager ) )
{
iReturn = pdTRUE;
}
else
{
iReturn = pdFALSE;
}
return iReturn;
}
/*-----------------------------------------------------------*/
int FF_FS_Get( int xIndex, FF_SubSystem_t *pxSystem )
{
int iReturn;
/* Get a copy of a fs info. */
if( ( xIndex < 0 ) || ( xIndex >= file_systems.xFileSystemCount ) )
{
iReturn = pdFALSE;
}
else
{
/* Note: it will copy the contents of 'FF_SubSystem_t'. */
*pxSystem = file_systems.xSystems[ xIndex ];
iReturn = pdTRUE;
}
return iReturn;
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,291 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "ff_time.h"
/**
* @file ff_time.c
* @ingroup TIME
*
* @defgroup TIME Real-Time Clock Interface
* @brief Allows FreeRTOS+FAT to time-stamp files.
*
* Provides a means for receiving the time on any platform.
**/
#if( ffconfigTIME_SUPPORT != 0 ) /* This if-block spans the rest of the source file. */
/**
* @public
* @brief Populates an FF_SystemTime_t object with the current time from the system.
*
* The developer must modify this function so that it is suitable for their platform.
* The function must return with 0, and if the time is not available all elements of the
* FF_SystemTime_t object must be zero'd, as in the examples provided.
*
* @param pxTime Pointer to an FF_TIME object.
*
* @return Always returns 0.
**/
int32_t FF_GetSystemTime( FF_SystemTime_t *pxTime )
{
FF_TimeStruct_t xTimeStruct;
/* Fetch the current time. */
time_t secs = FreeRTOS_time( NULL );
/* Fill the fields in 'xTimeStruct'. */
FreeRTOS_gmtime_r( &secs, &xTimeStruct );
pxTime->Hour = xTimeStruct.tm_hour;
pxTime->Minute = xTimeStruct.tm_min;
pxTime->Second = xTimeStruct.tm_sec;
pxTime->Day = xTimeStruct.tm_mday;
pxTime->Month = xTimeStruct.tm_mon + 1;
pxTime->Year = xTimeStruct.tm_year + 1900;
return 0;
} /* FF_GetSystemTime() */
/*-----------------------------------------------------------*/
/*
* FreeRTOS+FAT
* Time conversion functions:
*
* FF_TimeStruct_t *FreeRTOS_gmtime_r( const time_t *pxTime, FF_TimeStruct_t *pxTimeBuf )
* time_t FreeRTOS_mktime(FF_TimeStruct_t *pxTimeBuf)
*/
#define GMTIME_FIRST_YEAR ( 1970 )
#define TM_STRUCT_FIRST_YEAR ( 1900 )
#define SECONDS_PER_MINUTE ( 60 )
#define MINUTES_PER_HOUR ( 60 )
#define HOURS_PER_DAY ( 24 )
#define SECONDS_PER_HOUR ( MINUTES_PER_HOUR * SECONDS_PER_MINUTE )
#define SECONDS_PER_DAY ( HOURS_PER_DAY * SECONDS_PER_HOUR )
/* The first weekday in 'FF_TimeStruct_t' is Sunday. */
#define WEEK_DAY_SUNDAY 0
#define WEEK_DAY_MONNDAY 1
#define WEEK_DAY_TUESDAY 2
#define WEEK_DAY_WEDNESDAY 3
#define WEEK_DAY_THURSDAY 4
#define WEEK_DAY_FRIDAY 5
#define WEEK_DAY_SATURDAY 6
/* Make a bitmask with a '1' for each 31-day month. */
#define _MM(month) ( 1u << ( month - 1 ) )
#define MASK_LONG_MONTHS ( _MM(1) | _MM(3) | _MM(5) | _MM(7) | _MM(8) | _MM(10) | _MM(12) )
#define DAYS_UNTIL_1970 ( ( 1970 * 365 ) + ( 1970 / 4 ) - ( 1970 / 100 ) + ( 1970 / 400 ) )
#define DAYS_BEFORE_MARCH ( 59 )
static portINLINE int iIsLeapyear( int iYear )
{
int iReturn;
if( ( iYear % 4 ) != 0 )
{
/* Not a multiple of 4 years. */
iReturn = pdFALSE;
}
else if( ( iYear % 400 ) == 0 )
{
/* Every 4 centuries there is a leap year */
iReturn = pdTRUE;
}
else if( ( iYear % 100 ) == 0 )
{
/* Other centuries are not a leap year */
iReturn = pdFALSE;
}
else
{
/* Otherwise every fourth year. */
iReturn = pdTRUE;
}
return iReturn;
}
static portINLINE unsigned long ulDaysPerYear( int iYear )
{
int iDays;
if( iIsLeapyear( iYear ) )
{
iDays = 366;
}
else
{
iDays = 365;
}
return iDays;
}
static int iDaysPerMonth( int iYear, int iMonth )
{
int iDays;
/* Month is zero-based, 1 is February. */
if (iMonth != 1 )
{
/* 30 or 31 days? */
if( ( MASK_LONG_MONTHS & ( 1u << iMonth ) ) != 0 )
{
iDays = 31;
}
else
{
iDays = 30;
}
}
else if( iIsLeapyear( iYear ) == pdFALSE )
{
/* February, non leap year. */
iDays = 28;
}
else
{
/* February, leap year. */
iDays = 29;
}
return iDays;
}
FF_TimeStruct_t *FreeRTOS_gmtime_r( const time_t *pxTime, FF_TimeStruct_t *pxTimeBuf )
{
time_t xTime = *pxTime;
unsigned long ulDaySeconds, ulDayNumber;
int iYear = GMTIME_FIRST_YEAR;
int iMonth;
/* Clear all fields, some might not get set here. */
memset( ( void * )pxTimeBuf, '\0', sizeof( *pxTimeBuf ) );
/* Seconds since last midnight. */
ulDaySeconds = ( unsigned long ) ( xTime % SECONDS_PER_DAY ) ;
/* Days since 1 Jan 1970. */
ulDayNumber = ( unsigned long ) ( xTime / SECONDS_PER_DAY ) ;
/* Today's HH:MM:SS */
pxTimeBuf->tm_hour = ulDaySeconds / SECONDS_PER_HOUR;
pxTimeBuf->tm_min = ( ulDaySeconds % SECONDS_PER_HOUR ) / 60;
pxTimeBuf->tm_sec = ulDaySeconds % 60;
/* Today's week day, knowing that 1-1-1970 was a THursday. */
pxTimeBuf->tm_wday = ( ulDayNumber + WEEK_DAY_THURSDAY ) % 7;
for( ; ; )
{
/* Keep subtracting 365 (or 366) days while possible. */
unsigned long ulDays = ulDaysPerYear( iYear );
if( ulDayNumber < ulDays )
{
break;
}
ulDayNumber -= ulDays;
iYear++;
}
/* Subtract 1900. */
pxTimeBuf->tm_year = iYear - TM_STRUCT_FIRST_YEAR;
/* The day within this year. */
pxTimeBuf->tm_yday = ulDayNumber;
/* Month are counted as 0..11 */
iMonth = 0;
for( ; ; )
{
unsigned long ulDays = iDaysPerMonth( iYear, iMonth );
/* Keep subtracting 30 (or 28, 29, or 31) days while possible. */
if( ulDayNumber < ulDays )
{
break;
}
ulDayNumber -= ulDays;
iMonth++;
}
pxTimeBuf->tm_mon = iMonth;
/* Month days are counted as 1..31 */
pxTimeBuf->tm_mday = ulDayNumber + 1;
return pxTimeBuf;
}
time_t FreeRTOS_mktime( const FF_TimeStruct_t *pxTimeBuf )
{
/* Get year AD. */
int iYear = 1900 + pxTimeBuf->tm_year; /* 20xx */
/* Get month zero-based. */
int iMonth = pxTimeBuf->tm_mon; /* 0..11 */
uint32_t ulDays;
uint32_t ulResult;
ulDays = pxTimeBuf->tm_mday - 1; /* 1..31 */
/* Make March the first month. */
iMonth -= 2;
if( iMonth < 0 )
{
/* January or February: leap day has yet to come for this year. */
iYear--;
iMonth += 12;
}
/* Add the number of days past until this month. */
ulDays += ( ( 306 * iMonth ) + 5 ) / 10;
/* Add days past before this year: */
ulDays +=
+ ( iYear * 365 ) /* Every normal year. */
+ ( iYear / 4 ) /* Plus a day for every leap year. */
- ( iYear / 100 ) /* Minus the centuries. */
+ ( iYear / 400 ) /* Except every fourth century. */
- ( DAYS_UNTIL_1970 ) /* Minus the days before 1-1-1970 */
+ ( DAYS_BEFORE_MARCH );/* Because 2 months were subtracted. */
ulResult =
( ulDays * SECONDS_PER_DAY ) +
( pxTimeBuf->tm_hour * SECONDS_PER_HOUR ) +
( pxTimeBuf->tm_min * SECONDS_PER_MINUTE ) +
pxTimeBuf->tm_sec;
return ulResult;
}
#endif /* ffconfigTIME_SUPPORT */

View file

@ -0,0 +1,427 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
#ifndef FF_DEFAULTCONFIG_H
/* The error numbers defined in this file will be moved to the core FreeRTOS
code in future versions of FreeRTOS - at which time the following header file
will be removed. */
#include "FreeRTOS_errno_FAT.h"
#if !defined( ffconfigBYTE_ORDER )
/* Must be set to either pdFREERTOS_LITTLE_ENDIAN or pdFREERTOS_BIG_ENDIAN,
depending on the endian of the architecture on which FreeRTOS is running. */
#error Invalid FreeRTOSFATConfig.h file: ffconfigBYTE_ORDER must be set to either pdFREERTOS_LITTLE_ENDIAN or pdFREERTOS_BIG_ENDIAN
#endif
#if ( ffconfigBYTE_ORDER != pdFREERTOS_LITTLE_ENDIAN ) && ( ffconfigBYTE_ORDER != pdFREERTOS_BIG_ENDIAN )
#error Invalid FreeRTOSFATConfig.h file: ffconfigBYTE_ORDER must be set to either pdFREERTOS_LITTLE_ENDIAN or pdFREERTOS_BIG_ENDIAN
#endif
#if ( pdFREERTOS_LITTLE_ENDIAN != 0 ) || ( pdFREERTOS_BIG_ENDIAN != 1 )
#error Invalid projdefs.h or FreeRTOS_errno_FAT.h file
#endif
#if !defined( ffconfigHAS_CWD )
/* Set to 1 to maintain a current working directory (CWD) for each task that
accesses the file system, allowing relative paths to be used.
Set to 0 not to use a CWD, in which case full paths must be used for each
file access. */
#define ffconfigHAS_CWD 0
#if !defined( ffconfigCWD_THREAD_LOCAL_INDEX )
#error ffconfigCWD_THREAD_LOCAL_INDEX must be set to a free position within FreeRTOSs thread local storage pointer array for storage of a pointer to the CWD structure.
#endif
#endif
#if !defined( ffconfigLFN_SUPPORT )
/* Set to 1 to include long file name support. Set to 0 to exclude long
file name support.
If long file name support is excluded then only 8.3 file names can be used.
Long file names will be recognised but ignored.
Users should familiarise themselves with any patent issues that may
potentially exist around the use of long file names in FAT file systems
before enabling long file name support. */
#define ffconfigLFN_SUPPORT 0
#endif
#if !defined( ffconfigINCLUDE_SHORT_NAME )
/* Only used when ffconfigLFN_SUPPORT is set to 1.
Set to 1 to include a file's short name when listing a directory, i.e. when
calling findfirst()/findnext(). The short name will be stored in the
'pcShortName' field of FF_DirEnt_t.
Set to 0 to only include a file's long name. */
#define ffconfigINCLUDE_SHORT_NAME 0
#endif
#if !defined( ffconfigSHORTNAME_CASE )
/* Set to 1 to recognise and apply the case bits used by Windows XP+ when
using short file names - storing file names such as "readme.TXT" or
"SETUP.exe" in a short-name entry. This is the recommended setting for
maximum compatibility.
Set to 0 to ignore the case bits. */
#define ffconfigSHORTNAME_CASE 0
#endif
#if !defined( ipconfigQUICK_SHORT_FILENAME_CREATION )
/* This method saves a lot of time when creating directories with
many similar file names: when the short name version of a LFN already
exists, try at most 3 entries with a tilde:
README~1.TXT
README~2.TXT
README~3.TXT
After that create entries with pseudo-random 4-digit hex digits:
REA~E7BB.TXT
REA~BA32.TXT
REA~D394.TXT
*/
#define ipconfigQUICK_SHORT_FILENAME_CREATION 1
#endif
/* ASCII versus UNICODE, UTF-16 versus UTF-8 :
FAT directories, when using Long File Names, always store file and directory
names UTF-16 encoded.
The user can select how these names must be represented internally:
- ASCII (default)
- UTF-8 (ffconfigUNICODE_UTF8_SUPPORT = 1)
- UTF-16 (ffconfigUNICODE_UTF16_SUPPORT = 1)
*/
#if( ffconfigUNICODE_UTF16_SUPPORT == 0 )
/* Only used when ffconfigLFN_SUPPORT is set to 1.
Set to 1 to use UTF-16 (wide-characters) for file and directory names.
Set to 0 to use either 8-bit ASCII or UTF-8 for file and directory names
(see the ffconfigUNICODE_UTF8_SUPPORT). */
#define ffconfigUNICODE_UTF16_SUPPORT 0
#endif
#if !defined( ffconfigUNICODE_UTF8_SUPPORT )
/* Only used when ffconfigLFN_SUPPORT is set to 1.
Set to 1 to use UTF-8 encoding for file and directory names.
Set to 0 to use either 8-bit ASCII or UTF-16 for file and directory
names (see the ffconfig_UTF_16_SUPPORT setting). */
#define ffconfigUNICODE_UTF8_SUPPORT 0
#endif
#if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
#error Can not use both UTF-16 and UTF-8
#endif
#if !defined( ffconfigFAT12_SUPPORT )
/* Set to 1 to include FAT12 support.
Set to 0 to exclude FAT12 support.
FAT16 and FAT32 are always enabled. */
#define ffconfigFAT12_SUPPORT 0
#endif
#if !defined( ffconfigOPTIMISE_UNALIGNED_ACCESS )
/* When writing and reading data, i/o becomes less efficient if sizes other
than 512 bytes are being used. When set to 1 each file handle will
allocate a 512-byte character buffer to facilitate "unaligned access". */
#define ffconfigOPTIMISE_UNALIGNED_ACCESS 0
#endif
#if !defined( ffconfigCACHE_WRITE_THROUGH )
/* Input and output to a disk uses buffers that are only flushed at the
following times:
- When a new buffer is needed and no other buffers are available.
- When opening a buffer in READ mode for a sector that has just been changed.
- After creating, removing or closing a file or a directory.
Normally this is quick enough and it is efficient. If
ffconfigCACHE_WRITE_THROUGH is set to 1 then buffers will also be flushed each
time a buffer is released - which is less efficient but more secure. */
#define ffconfigCACHE_WRITE_THROUGH 0
#endif
#if !defined( ffconfigWRITE_BOTH_FATS )
/* In most cases, the FAT table has two identical copies on the disk,
allowing the second copy to be used in the case of a read error. If
Set to 1 to use both FATs - this is less efficient but more secure.
Set to 0 to use only one FAT - the second FAT will never be written to. */
#define ffconfigWRITE_BOTH_FATS 0
#endif
#if !defined( ffconfigWRITE_FREE_COUNT )
/* Set to 1 to have the number of free clusters and the first free cluster
to be written to the FS info sector each time one of those values changes.
Set to 0 not to store these values in the FS info sector, making booting
slower, but making changes faster. */
#define ffconfigWRITE_FREE_COUNT 0
#endif
#if !defined( ffconfigTIME_SUPPORT )
/* Set to 1 to maintain file and directory time stamps for creation, modify
and last access.
Set to 0 to exclude time stamps.
If time support is used, the following function must be supplied:
time_t FreeRTOS_time( time_t *pxTime );
FreeRTOS_time has the same semantics as the standard time() function. */
#define ffconfigTIME_SUPPORT 0
#endif
#if !defined( ffconfigREMOVABLE_MEDIA )
/* Set to 1 if the media is removable (such as a memory card).
Set to 0 if the media is not removable.
When set to 1 all file handles will be "invalidated" if the media is
extracted. If set to 0 then file handles will not be invalidated.
In that case the user will have to confirm that the media is still present
before every access. */
#define ffconfigREMOVABLE_MEDIA 0
#endif
#if !defined( ffconfigMOUNT_FIND_FREE )
/* Set to 1 to determine the disk's free space and the disk's first free
cluster when a disk is mounted.
Set to 0 to find these two values when they are first needed. Determining
the values can take some time. */
#define ffconfigMOUNT_FIND_FREE 0
#endif
#if !defined( ffconfigFSINFO_TRUSTED )
/* Set to 1 to 'trust' the contents of the 'ulLastFreeCluster' and
ulFreeClusterCount fields.
Set to 0 not to 'trust' these fields.*/
#define ffconfigFSINFO_TRUSTED 0
#endif
#if !defined( ffconfigFINDAPI_ALLOW_WILDCARDS )
/* For now must be set to 0. */
#define ffconfigFINDAPI_ALLOW_WILDCARDS 0
#endif
#if !defined( ffconfigWILDCARD_CASE_INSENSITIVE )
/* For now must be set to 0. */
#define ffconfigWILDCARD_CASE_INSENSITIVE 0
#endif
#if !defined( ffconfigPATH_CACHE )
/* Set to 1 to store recent paths in a cache, enabling much faster access
when the path is deep within a directory structure at the expense of
additional RAM usage.
Set to 0 to not use a path cache. */
#define ffconfigPATH_CACHE 0
#endif
#if !defined( ffconfigPATH_CACHE_DEPTH )
/* Only used if ffconfigPATH_CACHE is 1.
Sets the maximum number of paths that can exist in the patch cache at any
one time. */
#define ffconfigPATH_CACHE_DEPTH 5
#endif
#if !defined( ffconfigHASH_CACHE )
/* Set to 1 to calculate a HASH value for each existing short file name.
Use of HASH values can improve performance when working with large
directories, or with files that have a similar name.
Set to 0 not to calculate a HASH value. */
#define ffconfigHASH_CACHE 0
#endif
#if( ffconfigHASH_CACHE != 0 )
#if !defined( ffconfigHASH_FUNCTION )
/* Only used if ffconfigHASH_CACHE is set to 1
Set to CRC8 or CRC16 to use 8-bit or 16-bit HASH values respectively. */
#define ffconfigHASH_FUNCTION CRC16
#endif
#if ffconfigHASH_FUNCTION == CRC16
#define ffconfigHASH_TABLE_SIZE 8192
#elif ffconfigHASH_FUNCTION == CRC8
#define ffconfigHASH_TABLE_SIZE 32
#else
#error Invalid Hashing function selected. CRC16 or CRC8. See your FreeRTOSFATConfig.h.
#endif
#endif /* ffconfigHASH_CACHE != 0 */
#if !defined( ffconfigMKDIR_RECURSIVE )
/* Set to 1 to add a parameter to ff_mkdir() that allows an entire directory
tree to be created in one go, rather than having to create one directory in
the tree at a time. For example mkdir( "/etc/settings/network", pdTRUE );.
Set to 0 to use the normal mkdir() semantics (without the additional
parameter). */
#define ffconfigMKDIR_RECURSIVE 0
#endif
#if !defined( ffconfigMALLOC )
/* Set to a function that will be used for all dynamic memory allocations.
Setting to pvPortMalloc() will use the same memory allocator as FreeRTOS. */
#define ffconfigMALLOC( size ) pvPortMalloc( size )
#endif
#if !defined( ffconfigFREE )
/* Set to a function that matches the above allocator defined with
ffconfigMALLOC. Setting to vPortFree() will use the same memory free
function as FreeRTOS. */
#define ffconfigFREE( ptr ) vPortFree( ptr )
#endif
#if !defined( ffconfig64_NUM_SUPPORT )
/* Set to 1 to calculate the free size and volume size as a 64-bit number.
Set to 0 to calculate these values as a 32-bit number. */
#define ffconfig64_NUM_SUPPORT 0
#endif
#if !defined( ffconfigMAX_PARTITIONS )
/* Defines the maximum number of partitions (and also logical partitions)
that can be recognised. */
#define ffconfigMAX_PARTITIONS 4
#endif
#if !defined( ffconfigMAX_FILE_SYS )
/* Defines how many drives can be combined in total. Should be set to at
least 2. */
#define ffconfigMAX_FILE_SYS 4
#endif
#if !defined( ffconfigDRIVER_BUSY_SLEEP_MS )
/* In case the low-level driver returns an error 'FF_ERR_DRIVER_BUSY',
the library will pause for a number of ms, defined in
ffconfigDRIVER_BUSY_SLEEP_MS before re-trying. */
#define ffconfigDRIVER_BUSY_SLEEP_MS 20
#endif
#if !defined( ffconfigFPRINTF_SUPPORT )
/* Set to 1 to include the ff_fprintf() function.
Set to 0 to exclude the ff_fprintf() function.
ff_fprintf() is quite a heavy function because it allocates RAM and
brings in a lot of string and variable argument handling code. If
ff_fprintf() is not being used then the code size can be reduced by setting
ffconfigFPRINTF_SUPPORT to 0. */
#define ffconfigFPRINTF_SUPPORT 0
#endif
#if !defined( ffconfigFPRINTF_BUFFER_LENGTH )
/* ff_fprintf() will allocate a buffer of this size in which it will create
its formatted string. The buffer will be freed before the function
exits. */
#define ffconfigFPRINTF_BUFFER_LENGTH 128
#endif
#if !defined( ffconfigDEBUG )
#define ffconfigDEBUG 0
#endif
#if !defined( ffconfigLONG_ERR_MSG )
#define ffconfigLONG_ERR_MSG 0
#endif
#if( ffconfigDEBUG != 0 )
#if !defined( ffconfigHAS_FUNCTION_TAB )
#define ffconfigHAS_FUNCTION_TAB 1
#endif
#endif
#if !defined( ffconfigINLINE_MEMORY_ACCESS )
/* Set to 1 to inline some internal memory access functions.
Set to 0 to not inline the memory access functions. */
#define ffconfigINLINE_MEMORY_ACCESS 0
#endif
#if !defined( ffconfigMIRROR_FATS_UMOUNT )
/*_RB_ not sure. */
#define ffconfigMIRROR_FATS_UMOUNT 0
#endif
#if !defined( ffconfigFAT_CHECK )
/* Officially the only criteria to determine the FAT type (12, 16, or 32
bits) is the total number of clusters:
if( ulNumberOfClusters < 4085 ) : Volume is FAT12
if( ulNumberOfClusters < 65525 ) : Volume is FAT16
if( ulNumberOfClusters >= 65525 ) : Volume is FAT32
Not every formatted device follows the above rule.
Set to 1 to perform additional checks over and above inspecting the
number of clusters on a disk to determine the FAT type.
Set to 0 to only look at the number of clusters on a disk to determine the
FAT type. */
#define ffconfigFAT_CHECK 0
#endif
#if !defined( ffconfigMAX_FILENAME )
/* Sets the maximum length for file names, including the path.
Note that the value of this define is directly related to the maximum stack
use of the +FAT library. In some API's, a character buffer of size
'ffconfigMAX_FILENAME' will be declared on stack. */
#define ffconfigMAX_FILENAME 129
#endif
#if !defined( ffconfigUSE_DELTREE )
/* By default, do not include the recursive function ff_deltree() as
recursion breaches the coding standard - USE WITH CARE. */
#define ffconfigUSE_DELTREE 0
#endif
#if !defined(ffconfigFILE_EXTEND_FLUSHES_BUFFERS)
/* When writing large files, the contents of the FAT entries will be flushed
after every call to FF_Write(). This flushing can be inhibited, by defining
ffconfigFILE_EXTEND_FLUSHES_BUFFERS as 0. */
#define ffconfigFILE_EXTEND_FLUSHES_BUFFERS 1
#endif
#if !defined( FF_PRINTF )
#define FF_PRINTF FF_PRINTF
static portINLINE void FF_PRINTF( const char *pcFormat, ... )
{
( void ) pcFormat;
}
#endif
#endif

View file

@ -0,0 +1,90 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
#ifndef FREERTOS_ERRNO_FAT
#define FREERTOS_ERRNO_FAT
/* The following definitions will be included in the core FreeRTOS code in
future versions of FreeRTOS - hence the 'pd' (ProjDefs) prefix - at which time
this file will be removed. */
/* The following errno values are used by FreeRTOS+ components, not FreeRTOS
itself. */
/* For future compatibility (see comment above), check the definitions have not
already been made. */
#ifndef pdFREERTOS_ERRNO_NONE
#define pdFREERTOS_ERRNO_NONE 0 /* No errors */
#define pdFREERTOS_ERRNO_ENOENT 2 /* No such file or directory */
#define pdFREERTOS_ERRNO_EIO 5 /* I/O error */
#define pdFREERTOS_ERRNO_ENXIO 6 /* No such device or address */
#define pdFREERTOS_ERRNO_EBADF 9 /* Bad file number */
#define pdFREERTOS_ERRNO_EAGAIN 11 /* No more processes */
#define pdFREERTOS_ERRNO_EWOULDBLOCK 11 /* Operation would block */
#define pdFREERTOS_ERRNO_ENOMEM 12 /* Not enough memory */
#define pdFREERTOS_ERRNO_EACCES 13 /* Permission denied */
#define pdFREERTOS_ERRNO_EFAULT 14 /* Bad address */
#define pdFREERTOS_ERRNO_EBUSY 16 /* Mount device busy */
#define pdFREERTOS_ERRNO_EEXIST 17 /* File exists */
#define pdFREERTOS_ERRNO_EXDEV 18 /* Cross-device link */
#define pdFREERTOS_ERRNO_ENODEV 19 /* No such device */
#define pdFREERTOS_ERRNO_ENOTDIR 20 /* Not a directory */
#define pdFREERTOS_ERRNO_EISDIR 21 /* Is a directory */
#define pdFREERTOS_ERRNO_EINVAL 22 /* Invalid argument */
#define pdFREERTOS_ERRNO_ENOSPC 28 /* No space left on device */
#define pdFREERTOS_ERRNO_ESPIPE 29 /* Illegal seek */
#define pdFREERTOS_ERRNO_EROFS 30 /* Read only file system */
#define pdFREERTOS_ERRNO_EUNATCH 42 /* Protocol driver not attached */
#define pdFREERTOS_ERRNO_EBADE 50 /* Invalid exchange */
#define pdFREERTOS_ERRNO_EFTYPE 79 /* Inappropriate file type or format */
#define pdFREERTOS_ERRNO_ENMFILE 89 /* No more files */
#define pdFREERTOS_ERRNO_ENOTEMPTY 90 /* Directory not empty */
#define pdFREERTOS_ERRNO_ENAMETOOLONG 91 /* File or path name too long */
#define pdFREERTOS_ERRNO_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
#define pdFREERTOS_ERRNO_ENOBUFS 105 /* No buffer space available */
#define pdFREERTOS_ERRNO_ENOPROTOOPT 109 /* Protocol not available */
#define pdFREERTOS_ERRNO_EADDRINUSE 112 /* Address already in use */
#define pdFREERTOS_ERRNO_ETIMEDOUT 116 /* Connection timed out */
#define pdFREERTOS_ERRNO_EINPROGRESS 119 /* Connection already in progress */
#define pdFREERTOS_ERRNO_EALREADY 120 /* Socket already connected */
#define pdFREERTOS_ERRNO_EADDRNOTAVAIL 125 /* Address not available */
#define pdFREERTOS_ERRNO_EISCONN 127 /* Socket is already connected */
#define pdFREERTOS_ERRNO_ENOTCONN 128 /* Socket is not connected */
#define pdFREERTOS_ERRNO_ENOMEDIUM 135 /* No medium inserted */
#define pdFREERTOS_ERRNO_EILSEQ 138 /* An invalid UTF-16 sequence was encountered. */
#define pdFREERTOS_ERRNO_ECANCELED 140 /* Operation canceled. */
/* The following endian values are used by FreeRTOS+ components, not FreeRTOS
itself. */
#define pdFREERTOS_LITTLE_ENDIAN 0
#define pdFREERTOS_BIG_ENDIAN 1
#endif /* pdFREERTOS_ERRNO_NONE */
#endif /* FREERTOS_ERRNO_FAT */

View file

@ -0,0 +1,42 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
/**
* @file ff_crc.h
* @ingroup CRC
*
**/
#ifndef _FF_CRC_H_
#define _FF_CRC_H_
uint8_t FF_GetCRC8( uint8_t *pbyData, uint32_t stLength );
uint16_t FF_GetCRC16( uint8_t *pbyData, uint32_t stLength );
uint32_t FF_GetCRC32( uint8_t *pbyData, uint32_t stLength );
extern const uint32_t crc32_table[256];
#endif

View file

@ -0,0 +1,64 @@
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
*
*/
/**
* @file ff_devices.h
**/
#ifndef FF_DEVICES_H
#define FF_DEVICES_H
#ifndef PLUS_FAT_H
#error this header will be included from "plusfat.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define FF_DEV_NO_DEV 0
#define FF_DEV_CHAR_DEV 1
#define FF_DEV_BLOCK_DEV 2
BaseType_t xCheckDevicePath( const char *pcPath );
int FF_Device_Seek( FF_FILE *pxStream, long lOffset, int iWhence );
BaseType_t FF_Device_Open( const char *pcPath, FF_FILE * pxStream );
void FF_Device_Close( FF_FILE * pxStream );
size_t FF_Device_Read( void *pvBuf, size_t lSize, size_t lCount, FF_FILE * pxStream );
size_t FF_Device_Write( const void *pvBuf, size_t lSize, size_t lCount, FF_FILE * pxStream );
int FF_Device_GetDirEnt( const char *pcPath, FF_DirEnt_t *pxDirEnt );
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* FF_DEVICES_H */

Some files were not shown because too many files have changed in this diff Show more