diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2018-08-23 17:08:59 +0200 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2018-08-23 17:12:21 +0200 |
commit | 3061ecca3d0fdfb87dabbf5f63c9e06c2a30f53a (patch) | |
tree | ab49cc16ed0b853452c5c2ed2d3042416d628986 /thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot | |
download | iot-sensors-master.tar.gz iot-sensors-master.tar.bz2 iot-sensors-master.tar.xz iot-sensors-master.zip |
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot')
130 files changed, 42479 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.c new file mode 100644 index 0000000..8b08068 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.c @@ -0,0 +1,456 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup background_dfu_block background_dfu_block.c + * @{ + * @ingroup background_dfu + * @brief Background DFU block handling implementation. + * + */ + +#include "background_dfu_block.h" + +#include <assert.h> + +#include "sdk_config.h" +#include "app_scheduler.h" +#include "background_dfu_operation.h" +#include "compiler_abstraction.h" +#include "nrf_dfu_handling_error.h" + +#define NRF_LOG_MODULE_NAME background_dfu + +#define NRF_LOG_LEVEL BACKGROUND_DFU_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR BACKGROUND_DFU_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR BACKGROUND_DFU_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" + +#define BITMAP_BYTE_FROM_INDEX(index) ((index) / 8) +#define BITMAP_BIT_FROM_INDEX(index) (7 - ((index) % 8)) + +static void block_buffer_store(background_dfu_block_manager_t * p_bm); + +/**@brief Convert block number to bitmap index. + * + * @param[in] block_num Block number. + * + * @return Corresponding index. + */ +static __INLINE uint16_t block_num_to_index(uint32_t block_num) +{ + return block_num % BLOCKS_PER_BUFFER; +} + +/**@brief Set a bit in a bitmap. + * + * @param[inout] p_bitmap A pointer to the bitmap. + * @param[in] index Bit index to set. + */ +static __INLINE void set_bitmap_bit(uint8_t * p_bitmap, uint16_t index) +{ + p_bitmap[BITMAP_BYTE_FROM_INDEX(index)] |= (0x01 << BITMAP_BIT_FROM_INDEX(index)); +} + +/**@brief Clear a bit in a bitmap. + * + * @param[inout] p_bitmap A pointer to the bitmap. + * @param[in] index Bit index to clear. + */ +static __INLINE void clear_bitmap_bit(uint8_t * p_bitmap, uint16_t index) +{ + p_bitmap[BITMAP_BYTE_FROM_INDEX(index)] &= ~((uint8_t)(0x01 << BITMAP_BIT_FROM_INDEX(index))); +} + +/**@brief Check if a bit in a bitmap is set. + * + * @param[inout] p_bitmap A pointer to the bitmap. + * @param[in] index Bit index to check. + * + * @return True if bit is set, false otherwise. + */ +static __INLINE bool is_block_present(const uint8_t * p_bitmap, uint16_t index) +{ + return (p_bitmap[BITMAP_BYTE_FROM_INDEX(index)] >> BITMAP_BIT_FROM_INDEX(index)) & 0x01; +} + +/** + * @brief A callback function for DFU operation. + */ +static void dfu_operation_callback(nrf_dfu_response_t * p_res, void * p_context) +{ + background_dfu_block_manager_t * p_bm = (background_dfu_block_manager_t *)p_context; + ret_code_t res_code; + + if (p_res->result != NRF_DFU_RES_CODE_SUCCESS) + { + p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context); + } + else + { + switch (p_res->request) + { + case NRF_DFU_OP_OBJECT_CREATE: + { + // Object created, write respective block. + uint32_t current_size = p_bm->currently_stored_block * DEFAULT_BLOCK_SIZE; + uint16_t data_offset = block_num_to_index(p_bm->currently_stored_block) * DEFAULT_BLOCK_SIZE; + uint16_t store_size = MIN(DEFAULT_BLOCK_SIZE, (p_bm->image_size - current_size)); + res_code = background_dfu_op_write(p_bm->data + data_offset, + store_size, + dfu_operation_callback, + p_bm); + + if (res_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Failed to store block (b:%d c:%d).", + p_bm->currently_stored_block, + p_bm->current_block); + p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context); + } + + break; + } + + case NRF_DFU_OP_OBJECT_WRITE: + if (!((p_bm->currently_stored_block + 1) % BLOCKS_PER_DFU_OBJECT) || + ((p_bm->currently_stored_block + 1) == BLOCKS_PER_SIZE(p_bm->image_size))) + { + res_code = background_dfu_op_crc(dfu_operation_callback, p_bm); + + if (res_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Failed to store block (b:%d c:%d).", + p_bm->currently_stored_block, + p_bm->current_block); + p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context); + } + } + else + { + p_bm->last_block_stored = p_bm->currently_stored_block; + clear_bitmap_bit(p_bm->bitmap, block_num_to_index(p_bm->currently_stored_block)); + p_bm->currently_stored_block = INVALID_BLOCK_NUMBER; + + p_bm->result_handler(BACKGROUND_DFU_BLOCK_SUCCESS, p_bm->p_context); + + block_buffer_store(p_bm); + } + + break; + + case NRF_DFU_OP_CRC_GET: + res_code = background_dfu_op_execute(dfu_operation_callback, p_bm); + + if (res_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Failed to store block (b:%d c:%d).", + p_bm->currently_stored_block, + p_bm->current_block); + p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context); + } + + break; + + case NRF_DFU_OP_OBJECT_EXECUTE: + p_bm->last_block_stored = p_bm->currently_stored_block; + clear_bitmap_bit(p_bm->bitmap, block_num_to_index(p_bm->currently_stored_block)); + p_bm->currently_stored_block = INVALID_BLOCK_NUMBER; + + p_bm->result_handler(BACKGROUND_DFU_BLOCK_SUCCESS, p_bm->p_context); + + block_buffer_store(p_bm); + + break; + + default: + ASSERT(false); + } + } +} + +/**@brief Store a block from the buffer in a flash. + * + * @param[inout] p_bm A pointer to the block manager. + * @param[in] p_block A block number to store. + * + * @return NRF_SUCCESS on success, an error code is returned otherwise. + */ +static ret_code_t block_store(background_dfu_block_manager_t * p_bm, uint32_t block_num) +{ + p_bm->currently_stored_block = block_num; + + ret_code_t res_code = NRF_SUCCESS; + uint32_t current_size = block_num * DEFAULT_BLOCK_SIZE; + + do + { + // Initialize DFU object if needed. + if (!(block_num % BLOCKS_PER_DFU_OBJECT)) + { + uint32_t object_size = MIN(DEFAULT_DFU_OBJECT_SIZE, (p_bm->image_size - current_size)); + + res_code = background_dfu_op_create(p_bm->image_type, + object_size, + dfu_operation_callback, + p_bm); + break; + } + + // Store block. + uint16_t data_offset = block_num_to_index(block_num) * DEFAULT_BLOCK_SIZE; + uint16_t store_size = MIN(DEFAULT_BLOCK_SIZE, (p_bm->image_size - current_size)); + res_code = background_dfu_op_write(p_bm->data + data_offset, + store_size, + dfu_operation_callback, + p_bm); + + } while (0); + return res_code; +} + +/**@brief Check if block manager is busy storing a block. + * + * @param[inout] p_bm A pointer to the block manager. + * + */ +static bool is_block_manager_busy(background_dfu_block_manager_t * p_bm) +{ + return p_bm->currently_stored_block >= 0; +} + +/**@brief Store any valid blocks from the buffer in a flash. + * + * @param[inout] p_bm A pointer to the block manager. + * + */ +static void block_buffer_store(background_dfu_block_manager_t * p_bm) +{ + ret_code_t res_code = NRF_SUCCESS; + + if (!is_block_manager_busy(p_bm)) + { + if (p_bm->last_block_stored < p_bm->current_block) + { + int32_t block = p_bm->last_block_stored + 1; + + if (is_block_present(p_bm->bitmap, block_num_to_index(block))) + { + NRF_LOG_INFO("Storing block (b:%d c:%d).", block, p_bm->current_block); + + // There is a block to store. + res_code = block_store(p_bm, block); + if (res_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Failed to store block (b:%d c:%d).", block, p_bm->current_block); + p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context); + } + } + else + { + NRF_LOG_WARNING("Gap encountered - quit (b:%d c:%d).", block, p_bm->current_block); + } + } + } +} + +/** + * @brief A callback function for scheduling DFU block operations. + */ +static void block_store_scheduled(void * p_evt, uint16_t event_length) +{ + UNUSED_PARAMETER(event_length); + + background_dfu_block_manager_t * p_bm = *((background_dfu_block_manager_t **)p_evt); + block_buffer_store(p_bm); +} + +/**@brief Copy block data to the buffer. + * + * @param[inout] p_bm A pointer to the block manager. + * @param[in] p_block A pointer to the block. + */ +static void block_buffer_add(background_dfu_block_manager_t * p_bm, + const background_dfu_block_t * p_block) +{ + uint16_t index = block_num_to_index(p_block->number); + + memcpy(p_bm->data + index * DEFAULT_BLOCK_SIZE, p_block->p_payload, DEFAULT_BLOCK_SIZE); + set_bitmap_bit(p_bm->bitmap, index); + + if (p_bm->current_block < (int32_t)p_block->number) + { + p_bm->current_block = (int32_t)p_block->number; + } + + // Schedule block store. + UNUSED_RETURN_VALUE(app_sched_event_put(&p_bm, sizeof(p_bm), block_store_scheduled)); +} + +/*************************************************************************************************** + * @section Public + **************************************************************************************************/ + +void block_manager_init(background_dfu_block_manager_t * p_bm, + uint32_t object_type, + uint32_t object_size, + int32_t initial_block, + block_manager_result_notify_t result_handler, + void * p_context) +{ + p_bm->image_type = object_type; + p_bm->image_size = object_size; + p_bm->last_block_stored = p_bm->current_block = initial_block - 1; + p_bm->result_handler = result_handler; + p_bm->p_context = p_context; + p_bm->currently_stored_block = INVALID_BLOCK_NUMBER; + + memset(p_bm->bitmap, 0, sizeof(p_bm->bitmap)); +} + +background_dfu_block_result_t block_manager_block_process(background_dfu_block_manager_t * p_bm, + const background_dfu_block_t * p_block) +{ + /* + * Possible scenarios: + * 1) We receive a block older than our last stored block - simply ignore it. + * 2) We receive a block that fits within current buffer range - process it. + * 3) We receive a block that exceeds current buffer range - abort DFU as we won't be able to catch-up. + */ + + if (p_block->size != DEFAULT_BLOCK_SIZE) + { + NRF_LOG_WARNING("Block with incorrect size received (s:%d n:%d).", + p_block->size, p_block->number); + return BACKGROUND_DFU_BLOCK_IGNORE; + } + + if ((int32_t)p_block->number <= p_bm->last_block_stored) + { + NRF_LOG_WARNING("Ignoring block that already was stored(o:%d n:%d).", + p_bm->last_block_stored, p_block->number); + return BACKGROUND_DFU_BLOCK_IGNORE; + } + + if ((int32_t)p_block->number > p_bm->last_block_stored + BLOCKS_PER_BUFFER) + { + NRF_LOG_WARNING("Too many blocks missed - abort DFU (o:%d n:%d).", + p_bm->last_block_stored, p_block->number); + return BACKGROUND_DFU_BLOCK_INVALID; + } + + // Block fits within current buffer - copy it into the buffer and update the current block if most + // recent block was received. + block_buffer_add(p_bm, p_block); + + return BACKGROUND_DFU_BLOCK_SUCCESS; +} + +bool block_manager_is_image_complete(const background_dfu_block_manager_t * p_bm) +{ + uint32_t image_blocks = BLOCKS_PER_SIZE(p_bm->image_size); + + NRF_LOG_DEBUG("Is image complete (o:%d n:%d).", p_bm->last_block_stored, image_blocks); + + if (p_bm->last_block_stored + 1 == image_blocks) + { + return true; + } + + return false; +} + +bool block_manager_request_bitmap_get(const background_dfu_block_manager_t * p_bm, + background_dfu_request_bitmap_t * p_req_bmp) +{ + if (p_bm->current_block > p_bm->last_block_stored) + { + memset(p_req_bmp, 0, sizeof(*p_req_bmp)); + p_req_bmp->offset = p_bm->last_block_stored + 1; + p_req_bmp->size = (p_bm->current_block - p_bm->last_block_stored + 7) / 8; + + for (uint16_t block = p_req_bmp->offset; block <= p_bm->current_block; block++) + { + if (!is_block_present(p_bm->bitmap, block_num_to_index(block))) + { + set_bitmap_bit(p_req_bmp->bitmap, block - p_req_bmp->offset); + } + } + + // Clip empty bytes at the end. + while ((p_req_bmp->size > 0) && (p_req_bmp->bitmap[p_req_bmp->size - 1] == 0)) + { + p_req_bmp->size--; + } + + if (p_req_bmp->size == 0) + { + return false; + } + + return true; + } + + return false; +} + +bool block_manager_increment_current_block(background_dfu_block_manager_t * p_bm) +{ + uint32_t image_blocks = BLOCKS_PER_SIZE(p_bm->image_size); + + if (p_bm->current_block + 1 == image_blocks) + { + // Already on last block. + return false; + } + else + { + p_bm->current_block++; + } + + return true; +} + +int32_t block_manager_get_current_block(const background_dfu_block_manager_t * p_bm) +{ + return p_bm->current_block; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.h new file mode 100644 index 0000000..e1e468b --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.h @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup background_dfu_block background_dfu_block.H + * @{ + * @ingroup background_dfu + * @brief Background DFU block handling. + * + */ + +#ifndef BACKGROUND_DFU_BLOCK_H_ +#define BACKGROUND_DFU_BLOCK_H_ + +#include <stdint.h> +#include <stdbool.h> + +#include "app_util_platform.h" +#include "sdk_config.h" + +/** @brief Macro for calculating the number of blocks that fits in particular size. */ +#define BLOCKS_PER_SIZE(SIZE) ((SIZE + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE) + +/** @brief Default block size for background DFU blocks. */ +#define DEFAULT_BLOCK_SIZE BACKGROUND_DFU_DEFAULT_BLOCK_SIZE + +/** @brief Number of blocks in superblock. */ +#define BLOCKS_PER_BUFFER BACKGROUND_DFU_BLOCKS_PER_BUFFER + +/** @brief Size of the block buffer. Shall be a multiply of @ref DEFAULT_BLOCK_SIZE. */ +#define BLOCK_BUFFER_SIZE (BLOCKS_PER_BUFFER * DEFAULT_BLOCK_SIZE) + +/** @brief Size of the bitmap reflecting the state of the blocks in a superblock. */ +#define BITMAP_SIZE ((BLOCKS_PER_BUFFER + 7) / 8) + +/** @brief Default size of DFU object. Shall be a multiply of @ref DEFAULT_BLOCK_SIZE. */ +#define DEFAULT_DFU_OBJECT_SIZE 4096 + +/** @brief Number of blocks in DFU object. */ +#define BLOCKS_PER_DFU_OBJECT (BLOCKS_PER_SIZE(DEFAULT_DFU_OBJECT_SIZE)) + +/** @brief Value of invalid block number (for example to indicate that no block is being stored). */ +#define INVALID_BLOCK_NUMBER (-1) + +/** @brief Result of a DFU block operation. */ +typedef enum +{ + BACKGROUND_DFU_BLOCK_SUCCESS, /**< Block operation completed successfully. */ + BACKGROUND_DFU_BLOCK_IGNORE, /**< Block was ignored in current DFU context (i.e. duplicated block). */ + BACKGROUND_DFU_BLOCK_INVALID, /**< Block is invalid in current context, indicates that DFU shall be aborted. */ + BACKGROUND_DFU_BLOCK_STORE_ERROR /**< Block was not stored due to internal store error. */ +} background_dfu_block_result_t; + +/**@brief A function that module can register to receive block manager error notifications. */ +typedef void (* block_manager_result_notify_t)(background_dfu_block_result_t result, + void * p_context); + +/**@brief Block information structure. */ +typedef struct +{ + uint16_t size; /**< Size of the block in bytes. */ + uint32_t number; /**< Block number. */ + uint8_t * p_payload; /**< Block payload. */ +} background_dfu_block_t; + +/**@brief Block manager structure. + * + * Block manager keeps track of received blocks, ensuring that they are written into flash in + * a correct order, and updates the missing blocks bitmap, so that they could be requested from + * the server. + */ +typedef struct +{ + uint32_t image_size; /**< Size of currently stored image. */ + uint32_t image_type; /**< Image type (init command or firmware). */ + int32_t last_block_stored; /**< Number of the last block written in the flash. */ + int32_t current_block; /**< Last received (or expected) block. */ + uint8_t data[BLOCK_BUFFER_SIZE]; /**< Block buffer. */ + uint8_t bitmap[BITMAP_SIZE]; /**< A bitmap indicating which blocks have been received. */ + block_manager_result_notify_t result_handler; /**< A callback function for error notification. */ + void * p_context; /**< A context for result notification.*/ + int32_t currently_stored_block; /**< Number of block that is currently being stored. */ +} background_dfu_block_manager_t; + +/**@brief Bitmap structure used in bitmap requests. */ +typedef struct +{ + uint16_t size; /**< Size of the bitmap, in bytes.*/ + uint16_t offset; /**< Bitmap offset, indicating which block is referenced by first bit in bitmap. */ + uint8_t bitmap[BITMAP_SIZE]; /**< Bitmap itself. One in specific bit indicates which block is missing. */ +} background_dfu_request_bitmap_t; + +/**@brief Initialize block manager. + * + * @param[inout] p_bm A pointer to the block manager. + * @param[in] object_type Type of the image to store. + * @param[in] object_size Size of the image to store. + * @param[in] initial_block Number of the first block to receive. Typically it would be 0, but + * in case DFU restarted in the middle, it may differ. + * @param[in] error_handler A callback for error notification. + * @param[in] p_context A context for error notification. + */ +void block_manager_init(background_dfu_block_manager_t * p_bm, + uint32_t object_type, + uint32_t object_size, + int32_t initial_block, + block_manager_result_notify_t result_handler, + void * p_context); + +/**@brief Process a single block. + * + * @param[inout] p_bm A pointer to the block manager. + * @param[in] p_block A pointer to the block structure containing information about the block. + * + * @retval BACKGROUND_DFU_BLOCK_SUCCESS Block stored successfully. + * @retval BACKGROUND_DFU_BLOCK_IGNORE Invalid block size or block already stored in flash. + * @retval BACKGROUND_DFU_BLOCK_INVALID Block number indicates that too many blocks were missed. + * @retval BACKGROUND_DFU_BLOCK_STORE_ERROR Block store in flash failed. + */ +background_dfu_block_result_t block_manager_block_process(background_dfu_block_manager_t * p_bm, + const background_dfu_block_t * p_block); + +/**@brief Check if an image managed by a block manager is complete. + * + * @param[in] p_bm A pointer to the block manager. + * + * @return True if image is complete, false otherwise. + */ +bool block_manager_is_image_complete(const background_dfu_block_manager_t * p_bm); + +/**@brief Get current block bitmap. + * + * @param[in] p_bm A pointer to the block manager. + * @param[out] p_req_bmp A pointer to the block bitmap structure. + * + * @return True if non-empty bitmap was generated, false otherwise. + */ +bool block_manager_request_bitmap_get(const background_dfu_block_manager_t * p_bm, + background_dfu_request_bitmap_t * p_req_bmp); + +/**@brief Increment current block, in case no blocks were received and block timeout shot. + * + * @param[in] p_bm A pointer to the block manager. + * + * @return True if block was incremented, false if block manager is already on a last block of the image. + */ +bool block_manager_increment_current_block(background_dfu_block_manager_t * p_bm); + +/**@brief Get current block number that block manager received/expects. + * + * @param[in] p_bm A pointer to the block manager. + * + * @return Current block number. + */ +int32_t block_manager_get_current_block(const background_dfu_block_manager_t * p_bm); + +#endif /* BACKGROUND_DFU_BLOCK_H_ */ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.c new file mode 100644 index 0000000..7aa6679 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.c @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup background_dfu_operation background_dfu_operation.c + * @{ + * @ingroup background_dfu + * @brief Background DFU operations implementation. + * + */ + +#include "background_dfu_operation.h" + +#include "sdk_config.h" +#include "nrf_dfu_req_handler.h" + +#define NRF_LOG_MODULE_NAME background_dfu + +#define NRF_LOG_LEVEL BACKGROUND_DFU_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR BACKGROUND_DFU_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR BACKGROUND_DFU_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" + +ret_code_t background_dfu_op_select(uint32_t object_type, + nrf_dfu_response_callback_t callback, + void * p_context) +{ + nrf_dfu_request_t dfu_req; + + memset(&dfu_req, 0, sizeof(dfu_req)); + + dfu_req.request = NRF_DFU_OP_OBJECT_SELECT; + dfu_req.select.object_type = object_type; + dfu_req.p_context = p_context; + dfu_req.callback.response = callback; + + return nrf_dfu_req_handler_on_req(&dfu_req); +} + +ret_code_t background_dfu_op_create(uint32_t object_type, + uint32_t object_size, + nrf_dfu_response_callback_t callback, + void * p_context) +{ + nrf_dfu_request_t dfu_req; + + memset(&dfu_req, 0, sizeof(dfu_req)); + + dfu_req.request = NRF_DFU_OP_OBJECT_CREATE; + dfu_req.create.object_size = object_size; + dfu_req.create.object_type = object_type; + dfu_req.p_context = p_context; + dfu_req.callback.response = callback; + + return nrf_dfu_req_handler_on_req(&dfu_req); +} + +ret_code_t background_dfu_op_write(const uint8_t * p_payload, + uint16_t payload_length, + nrf_dfu_response_callback_t callback, + void * p_context) +{ + nrf_dfu_request_t dfu_req; + + memset(&dfu_req, 0, sizeof(dfu_req)); + + dfu_req.request = NRF_DFU_OP_OBJECT_WRITE; + dfu_req.write.p_data = (uint8_t *)p_payload; + dfu_req.write.len = payload_length; + dfu_req.p_context = p_context; + dfu_req.callback.response = callback; + + return nrf_dfu_req_handler_on_req(&dfu_req); +} + +ret_code_t background_dfu_op_crc(nrf_dfu_response_callback_t callback, + void * p_context) +{ + nrf_dfu_request_t dfu_req; + + memset(&dfu_req, 0, sizeof(dfu_req)); + + dfu_req.request = NRF_DFU_OP_CRC_GET; + dfu_req.p_context = p_context; + dfu_req.callback.response = callback; + + return nrf_dfu_req_handler_on_req(&dfu_req); +} + +ret_code_t background_dfu_op_execute(nrf_dfu_response_callback_t callback, + void * p_context) +{ + nrf_dfu_request_t dfu_req; + + memset(&dfu_req, 0, sizeof(dfu_req)); + + dfu_req.request = NRF_DFU_OP_OBJECT_EXECUTE; + dfu_req.p_context = p_context; + dfu_req.callback.response = callback; + + return nrf_dfu_req_handler_on_req(&dfu_req); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.h new file mode 100644 index 0000000..b01d9b2 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.h @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup background_dfu_operation background_dfu_operation.h + * @{ + * @ingroup background_dfu + * @brief Background DFU operations. + * + */ + +#ifndef BACKGROUND_DFU_OPERATION_H_ +#define BACKGROUND_DFU_OPERATION_H_ + +#include <stdint.h> + +#include "nrf_dfu_handling_error.h" + +/** @brief Select DFU object. + * + * @param[in] object_type Object type which should be selected. + * @param[in] callback A callback function to be executed after operation is completed. + * @param[in] p_context A pointer to the operation context. + * + * @return Operation result code. + */ +ret_code_t background_dfu_op_select(uint32_t object_type, + nrf_dfu_response_callback_t callback, + void * p_context); + +/** @brief Create DFU object. + * + * @param[in] object_type Object type which should be selected. + * @param[in] object_size Size of an object to create. + * @param[in] callback A callback function to be executed after operation is completed. + * @param[in] p_context A pointer to the operation context. + * + * @return Operation result code. + */ +ret_code_t background_dfu_op_create(uint32_t object_type, + uint32_t object_size, + nrf_dfu_response_callback_t callback, + void * p_context); + +/** @brief Write DFU object. + * + * @param[in] p_payload A pointer to data which should be written to the object. + * @param[in] payload_length Length, in bytes, of data which should be written to the object. + * @param[in] callback A callback function to be executed after operation is completed. + * @param[in] p_context A pointer to the operation context. + * + * @return Operation result code. + */ +ret_code_t background_dfu_op_write(const uint8_t * p_payload, + uint16_t payload_length, + nrf_dfu_response_callback_t callback, + void * p_context); + +/** @brief Calculate DFU object CRC. + * + * @param[in] callback A callback function to be executed after operation is completed. + * @param[in] p_context A pointer to the operation context. + * + * @return Operation result code. + */ +ret_code_t background_dfu_op_crc(nrf_dfu_response_callback_t callback, + void * p_context); + +/** @brief Execute selected DFU. + * + * @param[in] callback A callback function to be executed after operation is completed. + * @param[in] p_context A pointer to the operation context. + * + * @return Operation result code. + */ +ret_code_t background_dfu_op_execute(nrf_dfu_response_callback_t callback, + void * p_context); + +#endif /* BACKGROUND_DFU_OPERATION_H_ */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.c new file mode 100644 index 0000000..5004558 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.c @@ -0,0 +1,835 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup background_dfu_state background_dfu_state.c + * @{ + * @ingroup background_dfu + * @brief Background DFU state management. + * + */ + +#include "background_dfu_state.h" + +#include <string.h> + +#include "sdk_config.h" +#include "app_timer.h" +#include "compiler_abstraction.h" +#include "nrf_dfu_types.h" +#include "nrf_dfu_settings.h" +#include "sha256.h" +#include "background_dfu_transport.h" +#include "background_dfu_operation.h" + +#define NRF_LOG_MODULE_NAME background_dfu + +#define NRF_LOG_LEVEL BACKGROUND_DFU_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR BACKGROUND_DFU_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR BACKGROUND_DFU_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define BLOCK_REQUEST_JITTER_MIN 200 /**< Minimum jitter value when sending bitmap with requested blocks in multicast DFU. */ +#define BLOCK_REQUEST_JITTER_MAX 2000 /**< Maximum jitter value when sending bitmap with requested blocks in multicast DFU. */ +#define BLOCK_RECEIVE_TIMEOUT 2000 /**< Timeout value after which block is considered missing in multicast DFU. */ + +#define DFU_DATE_TIME (__DATE__ " " __TIME__) + +/**@brief DFU trigger packet version. */ +#define TRIGGER_VERSION 1 + +/** + * @defgroup background_dfu_trigger_flags Trigger flags and offsets. + * @{ + */ +#define TRIGGER_FLAGS_VERSION_OFFSET 4 +#define TRIGGER_FLAGS_VERSION_MASK 0xF0 +#define TRIGGER_FLAGS_MODE_OFFSET 3 +#define TRIGGER_FLAGS_MODE_MASK 0x08 +#define TRIGGER_FLAGS_RESET_OFFSET 2 +#define TRIGGER_FLAGS_RESET_MASK 0x04 +/** @} */ + +APP_TIMER_DEF(m_missing_block_timer); +APP_TIMER_DEF(m_block_timeout_timer); + +/**@brief Defines how many retries are performed in case no response is received. */ +#define DEFAULT_RETRIES 3 + +/**@brief DFU error handler. + * + * @param[inout] p_dfu_ctx DFU context. + */ +static __INLINE void dfu_handle_error(background_dfu_context_t * p_dfu_ctx) +{ + p_dfu_ctx->dfu_state = BACKGROUND_DFU_ERROR; + + background_dfu_handle_error(); +} + +/**@brief Get randomized jitter value. + * + * @return Randomized jitter value between BLOCK_REQUEST_JITTER_MIN and BLOCK_REQUEST_JITTER_MAX. + */ +static __INLINE uint32_t block_request_jitter_get(void) +{ + return BLOCK_REQUEST_JITTER_MIN + (background_dfu_random() % + (BLOCK_REQUEST_JITTER_MAX - BLOCK_REQUEST_JITTER_MIN)); +} + +/**@brief Starts block timeout timer. + * + * @param[inout] p_dfu_ctx DFU context. + */ +static __INLINE void start_block_timeout_timer(background_dfu_context_t * p_dfu_ctx) +{ + uint32_t err_code = app_timer_start(m_block_timeout_timer, + APP_TIMER_TICKS(BLOCK_RECEIVE_TIMEOUT), + p_dfu_ctx); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_start (%d)", err_code); + } +} + +/**@brief Stops block timeout timer. + * + * @param[inout] p_dfu_ctx DFU context. + */ +static __INLINE void stop_block_timeout_timer(background_dfu_context_t * p_dfu_ctx) +{ + UNUSED_PARAMETER(p_dfu_ctx); + uint32_t err_code = app_timer_stop(m_block_timeout_timer); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_stop (%d)", err_code); + } +} + +/**@brief Restarts block timeout timer. + * + * @param[inout] p_dfu_ctx DFU context. + */ +static __INLINE void restart_block_timeout_timer(background_dfu_context_t * p_dfu_ctx) +{ + stop_block_timeout_timer(p_dfu_ctx); + start_block_timeout_timer(p_dfu_ctx); +} + +/*************************************************************************************************** + * @section Handle DFU Trigger + **************************************************************************************************/ + +/**@brief Parses trigger data and updates DFU client context accordingly. + * + * @param[inout] p_dfu_ctx A pointer to DFU Client context. + * @param[in] p_trigger A pointer to trigger data. + * + * @return True if parsing was successful, false otherwise. + */ +static bool parse_trigger(background_dfu_context_t * p_dfu_ctx, + const background_dfu_trigger_t * p_trigger) +{ + uint8_t trigger_version = (p_trigger->flags & TRIGGER_FLAGS_VERSION_MASK) + >> TRIGGER_FLAGS_VERSION_OFFSET; + + if (trigger_version <= TRIGGER_VERSION) + { + // Base fields available from version 0. + p_dfu_ctx->init_cmd_size = uint32_big_decode((const uint8_t *)&p_trigger->init_length); + p_dfu_ctx->init_cmd_crc = uint32_big_decode((const uint8_t *)&p_trigger->init_crc); + p_dfu_ctx->firmware_size = uint32_big_decode((const uint8_t *)&p_trigger->image_length); + p_dfu_ctx->firmware_crc = uint32_big_decode((const uint8_t *)&p_trigger->image_crc); + + // Mode flag was added in DFU Trigger version 1. + if (trigger_version >= 1) + { + p_dfu_ctx->dfu_mode = (background_dfu_mode_t)((p_trigger->flags + & TRIGGER_FLAGS_MODE_MASK) >> TRIGGER_FLAGS_MODE_OFFSET); + p_dfu_ctx->reset_suppress = (p_trigger->flags & TRIGGER_FLAGS_RESET_MASK) >> + TRIGGER_FLAGS_RESET_OFFSET; + + } + else + { + p_dfu_ctx->dfu_mode = BACKGROUND_DFU_MODE_UNICAST; + } + + NRF_LOG_INFO("DFU trigger: init (sz=%d, crc=%0X) image (sz=%d, crc=%0X)", + p_dfu_ctx->init_cmd_size, + p_dfu_ctx->init_cmd_crc, + p_dfu_ctx->firmware_size, + p_dfu_ctx->firmware_crc); + + return true; + } + + return false; +} + +bool background_dfu_validate_trigger(background_dfu_context_t * p_dfu_ctx, + const uint8_t * p_payload, + uint32_t payload_len) +{ + if (payload_len != sizeof(background_dfu_trigger_t)) + { + NRF_LOG_ERROR("Validate trigger: size mismatch"); + return false; + } + + if ((p_dfu_ctx->dfu_state != BACKGROUND_DFU_IDLE) && + (p_dfu_ctx->dfu_state != BACKGROUND_DFU_DOWNLOAD_TRIG)) + { + NRF_LOG_ERROR("Validate trigger: DFU already in progress (s:%s).", + (uint32_t)background_dfu_state_to_string(p_dfu_ctx->dfu_state)); + return false; + } + + uint8_t trigger_version = (((background_dfu_trigger_t *)p_payload)->flags + & TRIGGER_FLAGS_VERSION_MASK) >> TRIGGER_FLAGS_VERSION_OFFSET; + if (trigger_version > TRIGGER_VERSION) + { + NRF_LOG_ERROR("Validate trigger: invalid trigger version."); + return false; + } + + return true; +} + +bool background_dfu_process_trigger(background_dfu_context_t * p_dfu_ctx, + const uint8_t * p_payload, + uint32_t payload_len) +{ + bool result = false; + + do + { + if (!parse_trigger(p_dfu_ctx, (background_dfu_trigger_t *)p_payload)) + { + NRF_LOG_ERROR("Process trigger: failed to parse payload"); + break; + } + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_DOWNLOAD_TRIG; + + uint32_t err_code = background_dfu_handle_event(p_dfu_ctx, + BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in background_dfu_handle_event (%d)", err_code); + } + + result = true; + } while(0); + + return result; +} + +/*************************************************************************************************** + * @section DFU checks + **************************************************************************************************/ + +background_dfu_block_result_t background_dfu_process_block(background_dfu_context_t * p_dfu_ctx, + const background_dfu_block_t * p_block) +{ + background_dfu_block_result_t result = block_manager_block_process(&p_dfu_ctx->block_manager, + p_block); + uint32_t err_code = NRF_SUCCESS; + + switch (result) + { + case BACKGROUND_DFU_BLOCK_IGNORE: + // Ignore. + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + restart_block_timeout_timer(p_dfu_ctx); + } + + break; + + case BACKGROUND_DFU_BLOCK_SUCCESS: + // Intentionally empty. + break; + + default: + err_code = background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_PROCESSING_ERROR); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in background_dfu_handle_event (%d)", err_code); + } + + break; + } + + return result; +} + +/**@brief Check if installed image is different from the incoming one. + * + * @param[in] p_dfu_ctx A pointer to DFU client context. + * + * @return True if image different, false otherwise. + * + */ +static bool is_image_different(const background_dfu_context_t * p_dfu_ctx) +{ + if (s_dfu_settings.bank_0.bank_code == NRF_DFU_BANK_INVALID) + { + NRF_LOG_WARNING("No image in bank 0"); + return true; + } + + if (s_dfu_settings.bank_0.image_crc != p_dfu_ctx->firmware_crc) + { + NRF_LOG_WARNING("Installed image CRC is different"); + return true; + } + + return false; +} + +/** + * @brief A callback function for block manager. + */ +static void dfu_block_manager_result_handler(background_dfu_block_result_t result, void * p_context) +{ + background_dfu_context_t * p_dfu_ctx = p_context; + uint32_t err_code; + + if (result == BACKGROUND_DFU_BLOCK_SUCCESS) + { + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + restart_block_timeout_timer(p_dfu_ctx); + } + + if (block_manager_is_image_complete(&p_dfu_ctx->block_manager)) + { + err_code = background_dfu_handle_event(p_dfu_ctx, + BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in background_dfu_handle_event (%d)", err_code); + } + } + else + { + // FIXME I don't like it here. + p_dfu_ctx->block_num++; + + err_code = background_dfu_handle_event(p_dfu_ctx, + BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in background_dfu_handle_event (%d)", err_code); + } + } + } + else + { + err_code = background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_PROCESSING_ERROR); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in background_dfu_handle_event (%d)", err_code); + } + } +} + +/** + * @brief Prepare state machine to download init command. + */ +static void setup_download_init_command(background_dfu_context_t * p_dfu_ctx) +{ + p_dfu_ctx->p_resource_size = &p_dfu_ctx->init_cmd_size; + p_dfu_ctx->retry_count = DEFAULT_RETRIES; + p_dfu_ctx->block_num = 0; + + background_dfu_transport_state_update(p_dfu_ctx); + + block_manager_init(&p_dfu_ctx->block_manager, + p_dfu_ctx->dfu_state, + *p_dfu_ctx->p_resource_size, + p_dfu_ctx->block_num, + dfu_block_manager_result_handler, + p_dfu_ctx); + + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + NRF_LOG_INFO("Init complete. Multicast Mode."); + uint32_t jitter = block_request_jitter_get(); + uint32_t err_code = app_timer_start(m_missing_block_timer, + APP_TIMER_TICKS(jitter), + p_dfu_ctx); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_start (%d)", err_code); + } + } + else + { + NRF_LOG_INFO("Init complete. Unicast Mode."); + } +} + +/** + * @brief A callback function for DFU command operations. + */ +static void dfu_init_check_callback(nrf_dfu_response_t * p_res, void * p_context) +{ + background_dfu_context_t * p_dfu_ctx = (background_dfu_context_t *)p_context; + + switch (p_res->request) + { + case NRF_DFU_OP_OBJECT_SELECT: + if (p_res->result != NRF_DFU_RES_CODE_SUCCESS) + { + NRF_LOG_ERROR("No valid init command - select failed"); + setup_download_init_command((background_dfu_context_t *)p_context); + + UNUSED_RETURN_VALUE(background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE)); + } + + p_dfu_ctx->max_obj_size = p_res->select.max_size; + p_dfu_ctx->block_num = p_res->select.offset / DEFAULT_BLOCK_SIZE; + + if (background_dfu_op_execute(dfu_init_check_callback, p_context) != NRF_SUCCESS) + { + NRF_LOG_ERROR("No valid init command - execute error"); + setup_download_init_command((background_dfu_context_t *)p_context); + + UNUSED_RETURN_VALUE(background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE)); + } + + break; + + case NRF_DFU_OP_OBJECT_EXECUTE: + if ((p_res->result != NRF_DFU_RES_CODE_SUCCESS) || + (s_dfu_settings.progress.command_crc != p_dfu_ctx->init_cmd_crc)) + { + NRF_LOG_ERROR("Init commad has changed"); + p_dfu_ctx->remaining_size = 0; + setup_download_init_command((background_dfu_context_t *)p_context); + + UNUSED_RETURN_VALUE(background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE)); + } + else + { + // Valid init command stored, download firmware. + p_dfu_ctx->dfu_diag.state = BACKGROUND_DFU_DOWNLOAD_INIT_CMD; + + UNUSED_RETURN_VALUE(background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE)); + } + + break; + + default: + ASSERT(false); + } +} + +/** + * @brief A callback function for DFU data operation. + */ +static void dfu_data_select_callback(nrf_dfu_response_t * p_res, void * p_context) +{ + ASSERT(p_res->request == NRF_DFU_OP_OBJECT_SELECT); + + background_dfu_context_t * p_dfu_ctx = (background_dfu_context_t *)p_context; + if (p_res->result != NRF_DFU_RES_CODE_SUCCESS) + { + NRF_LOG_ERROR("Select failed"); + dfu_handle_error(p_dfu_ctx); + return; + } + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_DOWNLOAD_FIRMWARE; + p_dfu_ctx->p_resource_size = &p_dfu_ctx->firmware_size; + p_dfu_ctx->retry_count = DEFAULT_RETRIES; + p_dfu_ctx->block_num = (p_res->select.offset / DEFAULT_BLOCK_SIZE); + p_dfu_ctx->max_obj_size = p_res->select.max_size; + + background_dfu_transport_state_update(p_dfu_ctx); + + block_manager_init(&p_dfu_ctx->block_manager, + p_dfu_ctx->dfu_state, + *p_dfu_ctx->p_resource_size, + p_dfu_ctx->block_num, + dfu_block_manager_result_handler, + p_dfu_ctx); + + UNUSED_RETURN_VALUE(background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE)); +} + +/*************************************************************************************************** + * @section Timer handlers + **************************************************************************************************/ + +/**@brief Handler function for block request timer. + * + * @param[inout] p_context DFU context. + */ +static void block_request_handler(void * p_context) +{ + background_dfu_context_t * p_dfu_ctx = (background_dfu_context_t *)p_context; + + if ((p_dfu_ctx->dfu_state != BACKGROUND_DFU_DOWNLOAD_FIRMWARE) && + (p_dfu_ctx->dfu_state != BACKGROUND_DFU_DOWNLOAD_INIT_CMD)) + { + return; + } + + background_dfu_request_bitmap_t req_bmp; + if (block_manager_request_bitmap_get(&p_dfu_ctx->block_manager, &req_bmp) && + (req_bmp.size > 0)) + { + background_dfu_transport_block_request_send(p_dfu_ctx, &req_bmp); + } + + // Reschedule the timer. + uint32_t jitter = block_request_jitter_get(); + uint32_t err_code = app_timer_start(m_missing_block_timer, APP_TIMER_TICKS(jitter), p_dfu_ctx); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_start (%d)", err_code); + } +} + +/**@brief Handler function for block timeout timer. + * + * @param[inout] p_context DFU context. + */ +static void block_timeout_handler(void * p_context) +{ + background_dfu_context_t * p_dfu_ctx = (background_dfu_context_t *)p_context; + + NRF_LOG_INFO("Block timeout! (b: %d)", + block_manager_get_current_block(&p_dfu_ctx->block_manager)); + + if ((p_dfu_ctx->dfu_state != BACKGROUND_DFU_DOWNLOAD_FIRMWARE) && + (p_dfu_ctx->dfu_state != BACKGROUND_DFU_DOWNLOAD_INIT_CMD)) + { + return; + } + + if (block_manager_increment_current_block(&p_dfu_ctx->block_manager)) + { + start_block_timeout_timer(p_dfu_ctx); + } +} + +/*************************************************************************************************** + * @section API functions + **************************************************************************************************/ + +/** @brief Helper function converting DFU state to string. + * + * @param[in] state DFU client state. + * + * @return A pointer to null terminated string with state name. + */ +const char * background_dfu_state_to_string(const background_dfu_state_t state) +{ + static const char * const names[] = + { + "DFU_DOWNLOAD_INIT_CMD", + "DFU_DOWNLOAD_FIRMWARE", + "DFU_DOWNLOAD_TRIG", + "DFU_WAIT_FOR_RESET", + "DFU_IDLE", + "DFU_ERROR", + }; + + return names[(uint32_t)state - BACKGROUND_DFU_DOWNLOAD_INIT_CMD]; +} + +/** @brief Helper function convering DFU event name to string. + * + * @param[in] state DFU client event. + * + * @return A pointer to null terminated string with event name. + */ +const char * background_dfu_event_to_string(const background_dfu_event_t event) +{ + static const char * const names[] = { + "DFU_EVENT_TRANSFER_COMPLETE", + "DFU_EVENT_TRANSFER_CONTINUE", + "DFU_EVENT_TRANSFER_ERROR", + "DFU_EVENT_PROCESSING_ERROR", + }; + + return names[event]; +} + +uint32_t background_dfu_handle_event(background_dfu_context_t * p_dfu_ctx, + background_dfu_event_t event) +{ + uint32_t err_code = NRF_SUCCESS; + + NRF_LOG_INFO("state=%s event=%s", + (uint32_t)background_dfu_state_to_string(p_dfu_ctx->dfu_state), + (uint32_t)background_dfu_event_to_string(event)); + + switch (p_dfu_ctx->dfu_state) + { + case BACKGROUND_DFU_IDLE: + { + if (event == BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE) + { + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_IDLE; + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_DOWNLOAD_TRIG; + p_dfu_ctx->block_num = 0; + p_dfu_ctx->retry_count = DEFAULT_RETRIES; + + background_dfu_transport_state_update(p_dfu_ctx); + } + + break; + } + + case BACKGROUND_DFU_DOWNLOAD_TRIG: + { + if (event == BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE) + { + if (!is_image_different(p_dfu_ctx)) + { + NRF_LOG_INFO("Image is already installed"); + background_dfu_reset_state(p_dfu_ctx); + break; + } + + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_DOWNLOAD_TRIG; + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_DOWNLOAD_INIT_CMD; + + // Initiate init command check procedure. + if (background_dfu_op_select(NRF_DFU_OBJ_TYPE_COMMAND, + dfu_init_check_callback, + p_dfu_ctx) != NRF_SUCCESS) + { + NRF_LOG_ERROR("No valid init command - select error"); + setup_download_init_command(p_dfu_ctx); + } + else + { + // We wait for dfu request to finish - do not send anything. + return NRF_SUCCESS; + } + } + + break; + } + + case BACKGROUND_DFU_DOWNLOAD_INIT_CMD: + { + if (event == BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE) + { + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_DOWNLOAD_INIT_CMD; + + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + stop_block_timeout_timer(p_dfu_ctx); + } + + if (background_dfu_op_select(NRF_DFU_OBJ_TYPE_DATA, + dfu_data_select_callback, + p_dfu_ctx) != NRF_SUCCESS) + { + NRF_LOG_ERROR("Select failed"); + dfu_handle_error(p_dfu_ctx); + err_code = NRF_ERROR_INTERNAL; + } + else + { + return NRF_SUCCESS; + } + } + else if (event == BACKGROUND_DFU_EVENT_PROCESSING_ERROR) + { + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_DOWNLOAD_INIT_CMD; + + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + stop_block_timeout_timer(p_dfu_ctx); + } + + NRF_LOG_ERROR("Processing error while downloading init command."); + dfu_handle_error(p_dfu_ctx); + } + break; + } + + case BACKGROUND_DFU_DOWNLOAD_FIRMWARE: + { + if (event == BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE) + { + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_DOWNLOAD_FIRMWARE; + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_WAIT_FOR_RESET; + + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + stop_block_timeout_timer(p_dfu_ctx); + } + + background_dfu_transport_state_update(p_dfu_ctx); + } + else if (event == BACKGROUND_DFU_EVENT_PROCESSING_ERROR) + { + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_DOWNLOAD_FIRMWARE; + + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + stop_block_timeout_timer(p_dfu_ctx); + } + + NRF_LOG_ERROR("Processing error while downloading firmware."); + dfu_handle_error(p_dfu_ctx); + } + break; + } + + case BACKGROUND_DFU_WAIT_FOR_RESET: + NRF_LOG_WARNING("An event received in wait for reset state. This should not happen."); + break; + + default: + NRF_LOG_ERROR("Unhandled state"); + break; + } + + if ((p_dfu_ctx->dfu_state != BACKGROUND_DFU_IDLE) && + (p_dfu_ctx->dfu_state != BACKGROUND_DFU_ERROR) && + (p_dfu_ctx->dfu_state != BACKGROUND_DFU_WAIT_FOR_RESET)) + { + if (((p_dfu_ctx->dfu_state == BACKGROUND_DFU_DOWNLOAD_FIRMWARE) || + (p_dfu_ctx->dfu_state == BACKGROUND_DFU_DOWNLOAD_INIT_CMD)) && + (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST)) + { + // In multicast DFU firmware download, client doesn't initiate block requests. + } + else + { + if ((event == BACKGROUND_DFU_EVENT_TRANSFER_ERROR) && (p_dfu_ctx->retry_count > 0)) + { + p_dfu_ctx->retry_count -= 1; + } + + if (p_dfu_ctx->retry_count > 0) + { + background_dfu_transport_send_request(p_dfu_ctx); + } + else + { + NRF_LOG_ERROR("No more retries"); + dfu_handle_error(p_dfu_ctx); + } + } + } + + return err_code; +} + +void background_dfu_reset_state(background_dfu_context_t * p_dfu_ctx) +{ + sha256_context_t sha256_ctx; + + uint8_t hash[32]; + uint32_t err_code = NRF_SUCCESS; + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_IDLE; + p_dfu_ctx->dfu_mode = BACKGROUND_DFU_MODE_UNICAST; + p_dfu_ctx->init_cmd_size = 0; + p_dfu_ctx->firmware_size = 0; + p_dfu_ctx->remaining_size = 0; + + memset(&p_dfu_ctx->dfu_diag, 0, sizeof(p_dfu_ctx->dfu_diag)); + + err_code = sha256_init(&sha256_ctx); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in sha256_init (%d)", err_code); + } + + err_code = sha256_update(&sha256_ctx, (const uint8_t *)DFU_DATE_TIME, strlen(DFU_DATE_TIME)); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in sha256_update (%d)", err_code); + } + + err_code = sha256_final(&sha256_ctx, (uint8_t *)hash, false); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in sha256_final (%d)", err_code); + } + + p_dfu_ctx->dfu_diag.build_id = uint32_big_decode(hash); + p_dfu_ctx->dfu_diag.state = BACKGROUND_DFU_IDLE; + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_IDLE; + + NRF_LOG_INFO("Current DFU Diag version: %s, 0x%08x", + (uint32_t)DFU_DATE_TIME, p_dfu_ctx->dfu_diag.build_id); +} + +void background_dfu_state_init(background_dfu_context_t * p_dfu_ctx) +{ + uint32_t err_code; + + err_code = app_timer_create(&m_missing_block_timer, + APP_TIMER_MODE_SINGLE_SHOT, + block_request_handler); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_create (%d)", err_code); + } + + err_code = app_timer_create(&m_block_timeout_timer, + APP_TIMER_MODE_SINGLE_SHOT, + block_timeout_handler); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_create (%d)", err_code); + } + + background_dfu_reset_state(p_dfu_ctx); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.h new file mode 100644 index 0000000..8dd4395 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.h @@ -0,0 +1,216 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup background_dfu_state background_dfu_state.h + * @{ + * @ingroup background_dfu + * @brief Background DFU state management. + * + */ + +#ifndef BACKGROUND_DFU_STATE_H_ +#define BACKGROUND_DFU_STATE_H_ + +#include <stdint.h> +#include <stdbool.h> + +#include "background_dfu_block.h" +#include "nrf_dfu_req_handler.h" + +/** @brief DFU client state ID. + * + * We reuse DFU object type IDs as DFU process states IDs, + * so that current state can be used as the object type in + * function which expect one. + */ +typedef enum +{ + BACKGROUND_DFU_DOWNLOAD_INIT_CMD = NRF_DFU_OBJ_TYPE_COMMAND, + BACKGROUND_DFU_DOWNLOAD_FIRMWARE = NRF_DFU_OBJ_TYPE_DATA, + BACKGROUND_DFU_DOWNLOAD_TRIG, + BACKGROUND_DFU_WAIT_FOR_RESET, + BACKGROUND_DFU_IDLE, + BACKGROUND_DFU_ERROR, +} background_dfu_state_t; + +/** @brief DFU event definitions. */ +typedef enum +{ + BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE, + BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE, + BACKGROUND_DFU_EVENT_TRANSFER_ERROR, + BACKGROUND_DFU_EVENT_PROCESSING_ERROR, +} background_dfu_event_t; + +/** @brief DFU mode definitions. */ +typedef enum +{ + BACKGROUND_DFU_MODE_UNICAST, + BACKGROUND_DFU_MODE_MULTICAST +} background_dfu_mode_t; + +/** @brief Trigger packet structure. */ +typedef PACKED_STRUCT +{ + uint8_t flags; /**< Trigger message flags. Bits 7:4 (oldest) - trigger version, bit 3 - DFU mode, bits 2:0 - reserved. */ + uint32_t init_length; + uint32_t init_crc; + uint32_t image_length; + uint32_t image_crc; +} background_dfu_trigger_t; + +/**@brief Structure with DFU diagnostic information. */ +typedef PACKED_STRUCT background_dfu_diagnostic +{ + uint32_t build_id; /**< Build identifier, based on compilation time. */ + uint8_t state; /**< Current DFU state. */ + uint8_t prev_state; /**< Previous DFU state. */ + uint16_t init_blocks_requested; /**< Number of requested missing init blocks. */ + uint16_t image_blocks_requested; /**< Number of requested missing image blocks. */ + uint16_t triggers_received; /**< Number of triggers received. */ + uint16_t total_init_blocks_received; /**< Total number of init blocks received, including retransmitted ones. */ + uint16_t total_image_blocks_received; /**< Total number of image blocks received, including retransmitted ones. */ +} background_dfu_diagnostic_t; + +/** @brief DFU client state. */ +typedef struct dfu_context +{ + background_dfu_state_t dfu_state; /**< Current DFU client state. */ + background_dfu_mode_t dfu_mode; /**< Current DFU mode. */ + bool reset_suppress; /**< If set then device won't automatically reset after + downloading firmware. */ + background_dfu_diagnostic_t dfu_diag; /**< DFU diagnostic information. */ + + uint32_t init_cmd_size; /**< Current init command size. */ + uint32_t init_cmd_crc; /**< Current init command checksum. */ + uint32_t firmware_size; /**< Current firmware command size. */ + uint32_t firmware_crc; /**< Current firmware command checksum. */ + uint32_t max_obj_size; /**< Maximum size of the DFU object. */ + uint32_t remaining_size; /**< Remaining size, in bytes, of the resource which + is being downloaded. */ + /* TODO Move the block num to the block manager. */ + uint32_t block_num; /**< Currently requested block number. */ + uint32_t * p_resource_size; /**< Downloaded resource size. */ + background_dfu_block_manager_t block_manager; /**< An entity managing block reception and storage. */ + uint8_t retry_count; /**< Number of remaining retires. */ +} background_dfu_context_t; + +/**@brief Check if payload contains valid trigger. + * + * @param[inout] p_dfu_ctx DFU context. + * @param[in] p_payload A pointer to the message payload. + * @param[in[ payload_len Payload length. + * + * @return True if trigger was valid, false otherwise. + */ +bool background_dfu_validate_trigger(background_dfu_context_t * p_dfu_ctx, + const uint8_t * p_payload, + uint32_t payload_len); + +/**@brief Process a payload with a DFU trigger. + * + * @param[inout] p_dfu_ctx DFU context. + * @param[in] p_payload A pointer to the message payload. + * @param[in[ payload_len Payload length. + * + * @return True if trigger was successfully processed, false otherwise. + */ +bool background_dfu_process_trigger(background_dfu_context_t * p_dfu_ctx, + const uint8_t * p_payload, + uint32_t payload_len); + +/**@brief Process the block and return CoAP result code corresponding to the result of operation. + * + * @param[inout] p_dfu_ctx DFU context. + * @param[in] p_block A pointer to the block structure. + * + * @return True if init command is valid, false otherwise. + * + * @retval BACKGROUND_DFU_BLOCK_SUCCESS The block was processed correctly. + * @retval BACKGROUND_DFU_BLOCK_IGNORE The block was incorrect for this node, but did not + * indicate that the DFU shall be stopped. + * @retval BACKGROUND_DFU_BLOCK_INVALID The block indicated that the node would not catch-up with the DFU + * transfer or other error occured. Node aborted DFU. + * @retval BACKGROUND_DFU_BLOCK_STORE_ERROR DFU store error. + */ +background_dfu_block_result_t background_dfu_process_block(background_dfu_context_t * p_dfu_ctx, + const background_dfu_block_t * p_block); + +/**@brief DFU state machine handler. + * + * @param[in] p_dfu_ctx DFU context. + * @param[in] event DFU event. + * + * @return NRF_SUCCESS or error code + */ +uint32_t background_dfu_handle_event(background_dfu_context_t * p_dfu_ctx, + background_dfu_event_t event); + +/**@brief Reset state machine state. + * + * @param[in] p_dfu_ctx A pointer to DFU client context. + */ +void background_dfu_reset_state(background_dfu_context_t * p_dfu_ctx); + +/**@brief Initialize state machine state. + * + * @param[in] p_dfu_ctx A pointer to DFU client context. + */ +void background_dfu_state_init(background_dfu_context_t * p_dfu_ctx); + +/**@brief Convert a DFU event enum value to a string description. + * + * @param[in] event A DFU event. + * + * @return String representing the event. + */ +const char * background_dfu_event_to_string(const background_dfu_event_t event); + +/**@brief Convert a DFU state enum value to a string description. + * + * @param[in] event A DFU state. + * + * @return String representing the state. + */ +const char * background_dfu_state_to_string(const background_dfu_state_t state); + +#endif /* BACKGROUND_DFU_STATE_H_ */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/background_dfu_transport.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/background_dfu_transport.h new file mode 100644 index 0000000..061a921 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/background_dfu_transport.h @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup background_dfu_transport background_dfu_state.h + * @{ + * @ingroup background_dfu + * @brief Background DFU transport API. + * + */ + +#ifndef BACKGROUND_DFU_TRANSPORT_H_ +#define BACKGROUND_DFU_TRANSPORT_H_ + +#include "background_dfu_state.h" + +/**@brief Create and send DFU block request with missing blocks. + * + * This function is used in multicast DFU. + * + * @param[in] p_dfu_ctx A pointer to the background DFU context. + * @param[in] p_req_bmp A pointer to the bitmap structure that shall be sent. + */ +void background_dfu_transport_block_request_send(background_dfu_context_t * p_dfu_ctx, + background_dfu_request_bitmap_t * p_req_bmp); + +/**@brief Send background DFU request, based on DFU state. + * + * @param[in] p_dfu_ctx A pointer to the background DFU context. + */ +void background_dfu_transport_send_request(background_dfu_context_t * p_dfu_ctx); + +/**@brief Update background DFU transport state. + * + * @param[in] p_dfu_ctx A pointer to the background DFU context. + */ +void background_dfu_transport_state_update(background_dfu_context_t * p_dfu_ctx); + +/**@brief Get random value. + * + * @returns A random value of uint32_t type. + */ +uint32_t background_dfu_random(void); + +/** @brief Handle DFU error. + * + * Notify transport about DFU error. + */ +void background_dfu_handle_error(void); + +#endif /* BACKGROUND_DFU_COAP_H_ */ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.c new file mode 100644 index 0000000..923d220 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.c @@ -0,0 +1,928 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @brief TFTP DFU Example - Background DFU transport implementation. + * + */ +#include "tftp_dfu.h" + +#include "background_dfu_transport.h" + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include "app_timer.h" +#include "background_dfu_block.h" +#include "background_dfu_transport.h" +#include "cJSON.h" +#include "cJSON_iot_hooks.h" +#include "crc16.h" +#include "iot_file_static.h" +#include "iot_tftp.h" +#include "nrf.h" +#include "nrf_assert.h" +#include "nrf_delay.h" +#include "nrf_dfu_req_handler.h" +#include "nrf_dfu_settings.h" +#include "nrf_dfu_utils.h" +#include "nrf_log_ctrl.h" + +#define NRF_LOG_LEVEL 4 +#define NRF_LOG_MODULE_NAME TFTP_DFU +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define MAX_LENGTH_FILENAME 32 /**< Maximum length of the filename. */ +#define MAX_CONFIG_SIZE 1024 /**< Maximum DFU of the config size. */ + +#define CONFIG_APP_KEY "app" +#define CONFIG_SD_KEY "sd" +#define CONFIG_APPSD_KEY "appsd" +#define CONFIG_BL_KEY "bl" +#define CONFIG_PATH_KEY "p" +#define CONFIG_INIT_PATH_KEY "i" +#define CONFIG_SIZE_KEY "s" +#define CONFIG_ID_KEY "id" +#define CONFIG_INIT_SIZE_KEY "is" +#define CONFIG_INIT_ID_KEY "iid" + +#define APP_TFTP_BLOCK_SIZE 512 /**< Maximum or negotiated size of data block. */ +#define APP_TFTP_RETRANSMISSION_TIME 3 /**< Number of milliseconds between retransmissions. */ + +#define BOOTLOADER_REGION_START 0x0007D000 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. + This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. + The value is used to determine max application size for updating. */ + +#define TFTP_SOFTDEVICE_UPGRADE_SUPPORT false /**< Upgrade of softdevice or bootloader is not supported with current version of Background DFU module. */ + +/** The device info offset can be modified to place the device info settings at a different location. + * If the customer reserved UICR location is used for other application specific data, the offset + * must be updated to avoid collision with that data.: + */ +/** [DFU UICR DEV offset] */ +#define UICR_CUSTOMER_DEVICE_INFO_OFFSET 0x0 /**< Device info offset inside the customer UICR reserved area. Customers may change this value to place the device information in a user-preferred location. */ +/** [DFU UICR DEV offset] */ +#define UICR_CUSTOMER_RESERVED_OFFSET 0x80 /**< Customer reserved area in the UICR. The area from UICR + 0x80 is reserved for customer usage. */ +#define DFU_DEVICE_INFO_BASE (NRF_UICR_BASE + \ + UICR_CUSTOMER_RESERVED_OFFSET + \ + UICR_CUSTOMER_DEVICE_INFO_OFFSET) /**< The device information base address inside of UICR. */ +#define DFU_DEVICE_INFO ((dfu_device_info_t *)DFU_DEVICE_INFO_BASE) /**< The memory mapped structure for device information data. */ + + +/**@brief Structure holding basic device information settings. + */ +typedef struct +{ + uint16_t device_type; /**< Device type (2 bytes), for example Heart Rate. This number must be defined by the customer before production. It can be located in UICR or FICR. */ + uint16_t device_rev; /**< Device revision (2 bytes), for example major revision 1, minor revision 0. This number must be defined by the customer before production. It can be located in UICR or FICR. */ +} dfu_device_info_t; + +/**@brief Description of single block of firmware. */ +typedef struct +{ + uint32_t size; /**< Size of firmware block. */ + uint32_t crc; /**< CRC of firmware block. Set to 0 if no checksum checking is needed. */ + uint32_t init_size; /**< Size of init file. */ + uint32_t init_crc; /**< CRC of init file. */ +} iot_dfu_firmware_block_t; + + +/**@brief Description of the new firmware that has been written into flash memory. */ +typedef struct +{ + iot_dfu_firmware_block_t application; /**< Description of Application block in firmware image. */ + iot_dfu_firmware_block_t softdevice; /**< Description of SoftDevice block in firmware image. */ + iot_dfu_firmware_block_t bootloader; /**< Description of Bootloader block in firmware image. */ +} iot_dfu_firmware_desc_t; + +/**@brief Type of image being updated by the DFU module. */ +typedef enum +{ + TFTP_DFU_IMAGE_TYPE_APPLICATION, /**< DFU updates application. */ + TFTP_DFU_IMAGE_TYPE_SOFTDEVICE, /**< DFU updates softdevice and application. */ + TFTP_DFU_IMAGE_TYPE_BOOTLOADER, /**< DFU updates bootloader. */ +} tftp_dfu_image_type_t; + +/**@brief Static data used by TFTP DFU module. */ +typedef struct +{ + iot_tftp_t tftp; /**< TFTP instance. */ + char resource_path[MAX_LENGTH_FILENAME]; /**< Path of remote resource to get. */ + uint32_t block_number; /**< Number of block that will be passed to background DFU module. */ + uint8_t block[DEFAULT_BLOCK_SIZE]; /**< Buffer for parts of blocks to pass to background DFU module. */ + uint16_t block_size; /**< Size of data in block buffer. */ + tftp_dfu_image_type_t image_type; /**< Type of image that is currently updated. */ + iot_file_t config_file; /**< Pointer to the file used for config of DFU. */ + uint8_t config_mem[MAX_CONFIG_SIZE]; /**< Static memory for configuration file. */ + cJSON * p_config_json; /**< Pointer of cJSON instance. */ + iot_dfu_firmware_desc_t firmware_desc; /**< Details from configuration file. */ +} tftp_dfu_context_t; + +static background_dfu_context_t m_dfu_ctx; /**< Background DFU context. */ +static tftp_dfu_context_t m_tftp_dfu_ctx; /**< TFTP DFU context. */ + +/*************************************************************************************************** + * @section Common operations + **************************************************************************************************/ + +/**@brief Set resource path to trigger (config) file. */ +static void trigger_path_set(void) +{ + int retval = snprintf(m_tftp_dfu_ctx.resource_path, + sizeof(m_tftp_dfu_ctx.resource_path), + "/dfu/c/%d/%d", + DFU_DEVICE_INFO->device_type, + DFU_DEVICE_INFO->device_rev); + if (retval < 0) { + NRF_LOG_ERROR("Failed to set path using snprintf, retval: %d", retval); + } +} + +/**@brief Function for reading binary path from JSON configuration. + * + * @param[out] p_dst_str Pointer to memory, where path should be stored. + * @param[in] p_key Key inside JSON configuration file, describing firmware. + * + * @return NRF_SUCCESS if path found and stored in p_dst_str, otherwise error code. + */ +static uint32_t get_path(char * p_dst_str, const char * p_key) +{ + cJSON * p_cursor; + + if ((m_tftp_dfu_ctx.p_config_json == NULL) || (p_dst_str == NULL)) + { + NRF_LOG_ERROR("Invalid parameters"); + return NRF_ERROR_INVALID_PARAM; + } + + p_cursor = cJSON_GetObjectItem(m_tftp_dfu_ctx.p_config_json, CONFIG_PATH_KEY); + if (p_cursor != NULL) + { + p_cursor = cJSON_GetObjectItem(p_cursor, (const char *)p_key); + if ((p_cursor != NULL) && (p_cursor->type == cJSON_String)) + { + memcpy(p_dst_str, p_cursor->valuestring, strlen(p_cursor->valuestring)); + } + else + { + return NRF_ERROR_INVALID_PARAM; + } + } + else + { + return NRF_ERROR_INVALID_PARAM; + } + + return NRF_SUCCESS; +} + +/**@brief Function for reading init binary path from JSON configuration. + * + * @param[out] p_dst_str Pointer to memory, where path should be stored. + * @param[in] p_key Key inside JSON configuration file, describing firmware. + * + * @return NRF_SUCCESS if path found and stored in p_dst_str, otherwise error code. + */ +static uint32_t get_init_path(char * p_dst_str, const char * p_key) +{ + cJSON * p_cursor; + + if ((m_tftp_dfu_ctx.p_config_json == NULL) || (p_dst_str == NULL)) + { + NRF_LOG_ERROR("Invalid parameters"); + return NRF_ERROR_INVALID_PARAM; + } + + p_cursor = cJSON_GetObjectItem(m_tftp_dfu_ctx.p_config_json, CONFIG_INIT_PATH_KEY); + if (p_cursor != NULL) + { + p_cursor = cJSON_GetObjectItem(p_cursor, (const char *)p_key); + if ((p_cursor != NULL) && (p_cursor->type == cJSON_String)) + { + memcpy(p_dst_str, p_cursor->valuestring, strlen(p_cursor->valuestring)); + } + else + { + return NRF_ERROR_INVALID_PARAM; + } + } + else + { + return NRF_ERROR_INVALID_PARAM; + } + + return NRF_SUCCESS; +} + +/**@brief Function for parsing JSON file to get details of an application. + * + * @param[in] p_key Key inside JSON configuration file, describing firmware. + * @param[out] p_block Pointer to structure containing description of single block of firmware. + * + * @returns NRF_SUCCESS if correctly parsed data. Otherwise an error code indicating failure reason. + */ +static uint32_t details_parse(const char * p_key, iot_dfu_firmware_block_t * p_block) +{ + cJSON * p_cursor; + cJSON * p_cursor_back; + + // Clear output parameters. + memset(p_block, 0, sizeof(*p_block)); + + if (m_tftp_dfu_ctx.p_config_json == NULL) + { + NRF_LOG_ERROR("Invalid JSON file"); + return NRF_ERROR_INVALID_DATA; + } + + p_cursor = cJSON_GetObjectItem(m_tftp_dfu_ctx.p_config_json, (const char *)p_key); + if (p_cursor != NULL) + { + p_cursor_back = p_cursor; + p_cursor = cJSON_GetObjectItem(p_cursor, CONFIG_ID_KEY); + if (p_cursor != NULL) + { + if (p_cursor->type != cJSON_Number) + { + return NRF_ERROR_INVALID_DATA; + } + + p_block->crc = p_cursor->valueint; + } + else + { + NRF_LOG_ERROR("No binary ID inside JSON."); + return NRF_ERROR_INVALID_DATA; + } + + p_cursor = p_cursor_back; + p_cursor = cJSON_GetObjectItem(p_cursor, CONFIG_INIT_SIZE_KEY); + if (p_cursor != NULL) + { + if (p_cursor->type != cJSON_Number) + { + return NRF_ERROR_INVALID_DATA; + } + + p_block->init_size = p_cursor->valueint; + } + else + { + NRF_LOG_ERROR("No init SIZE inside JSON."); + return NRF_ERROR_INVALID_DATA; + } + + p_cursor = p_cursor_back; + p_cursor = cJSON_GetObjectItem(p_cursor, CONFIG_INIT_ID_KEY); + if (p_cursor != NULL) + { + if (p_cursor->type != cJSON_Number) + { + return NRF_ERROR_INVALID_DATA; + } + + p_block->init_crc = p_cursor->valueint; + } + else + { + NRF_LOG_ERROR("No init ID inside JSON."); + return NRF_ERROR_INVALID_DATA; + } + + p_cursor = p_cursor_back; + p_cursor = cJSON_GetObjectItem(p_cursor, CONFIG_SIZE_KEY); + if (p_cursor != NULL) + { + if ((p_cursor->type != cJSON_Number) || (p_cursor->valueint == 0)) + { + return NRF_ERROR_INVALID_DATA; + } + + p_block->size = p_cursor->valueint; + } + else + { + NRF_LOG_ERROR("No binary SIZE inside JSON."); + return NRF_ERROR_INVALID_DATA; + } + } + else + { + NRF_LOG_ERROR("No binary KEY inside JSON."); + return NRF_ERROR_INVALID_DATA; + } + + return NRF_SUCCESS; +} + + +/**@brief Function for parsing and processing config file. + * + * @param[out] p_dfu_firmware_desc Details of available firmware images (size and CRC) + * @param[out] p_filename Remote path to init file that should be downloaded in DFU process + * + * @return NRF_SUCCESS If config file is valid. Otherwise an error code indicating failure reason. + */ +static uint32_t app_dfu_config_process(iot_dfu_firmware_desc_t * p_dfu_firmware_desc, + char * p_filename) +{ + uint32_t err_code = NRF_SUCCESS; + iot_dfu_firmware_block_t app; +#if TFTP_SOFTDEVICE_UPGRADE_SUPPORT + iot_dfu_firmware_block_t sd; + iot_dfu_firmware_block_t bl; +#endif // TFTP_SOFTDEVICE_UPGRADE_SUPPORT + bool app_present = false; +#if TFTP_SOFTDEVICE_UPGRADE_SUPPORT + bool sd_present = false; + bool bl_present = false; +#endif // TFTP_SOFTDEVICE_UPGRADE_SUPPORT + + // Clear global parameters before parsing a new one. + memset(p_filename, 0, MAX_LENGTH_FILENAME); + + m_tftp_dfu_ctx.p_config_json = cJSON_Parse((const char *)m_tftp_dfu_ctx.config_mem); + if (m_tftp_dfu_ctx.p_config_json == NULL) + { + NRF_LOG_ERROR("JSON parse failed."); + return NRF_ERROR_INVALID_DATA; + } + +#if TFTP_SOFTDEVICE_UPGRADE_SUPPORT + if (details_parse(CONFIG_SD_KEY, &sd) == NRF_SUCCESS) + { + sd_present = true; + } + + if (details_parse(CONFIG_BL_KEY, &bl) == NRF_SUCCESS) + { + bl_present = true; + } +#endif // TFTP_SOFTDEVICE_UPGRADE_SUPPORT + + if (details_parse(CONFIG_APP_KEY, &app) == NRF_SUCCESS) + { + app_present = true; + } + +#if TFTP_SOFTDEVICE_UPGRADE_SUPPORT + // Background DFU does not support SoftDevice or bootloader update yet. + if ((sd_present) && (app_present)) + { + NRF_LOG_INFO("Update Softdevice with Application."); + m_tftp_dfu_ctx.image_type = TFTP_DFU_IMAGE_TYPE_SOFTDEVICE; + } + else if (bl_present) + { + NRF_LOG_INFO("Update Bootloader."); + m_tftp_dfu_ctx.image_type = TFTP_DFU_IMAGE_TYPE_BOOTLOADER; + } + else +#endif // TFTP_SOFTDEVICE_UPGRADE_SUPPORT + { + if (app_present) + { + NRF_LOG_INFO("Update Application only."); + m_tftp_dfu_ctx.image_type = TFTP_DFU_IMAGE_TYPE_APPLICATION; + } + else + { + // This example application does not implement SoftDevice with Bootloader update + NRF_LOG_INFO("Device firmware up to date."); + err_code = NRF_ERROR_NOT_FOUND; + } + } + + if (err_code == NRF_SUCCESS) + { + switch (m_tftp_dfu_ctx.image_type) + { + case TFTP_DFU_IMAGE_TYPE_APPLICATION: + err_code = get_init_path(p_filename, CONFIG_APP_KEY); + if (err_code == NRF_SUCCESS) + { + p_dfu_firmware_desc->application = app; + } + + break; + +#if TFTP_SOFTDEVICE_UPGRADE_SUPPORT + case TFTP_DFU_IMAGE_TYPE_BOOTLOADER: + err_code = get_init_path(p_filename, CONFIG_BL_KEY); + if (err_code == NRF_SUCCESS) + { + p_dfu_firmware_desc->bootloader = bl; + } + + break; + + case TFTP_DFU_IMAGE_TYPE_SOFTDEVICE: + err_code = get_init_path(p_filename, CONFIG_APPSD_KEY); + if (err_code == NRF_SUCCESS) + { + p_dfu_firmware_desc->softdevice = sd; + p_dfu_firmware_desc->application = app; + } + + break; +#endif // TFTP_SOFTDEVICE_UPGRADE_SUPPORT + + default: + ASSERT(false); + } + } + + if (p_filename[0] == '\0') + { + NRF_LOG_ERROR("File name has not be found."); + err_code = NRF_ERROR_INVALID_PARAM; + } + + cJSON_Delete(m_tftp_dfu_ctx.p_config_json); + + return err_code; +} + +/** @brief Set resource path to point to firmware binary file. + * + * @param [out] p_filename Pointer to resource path. + * + * @return NRF_SUCCESS if setting firmware path succeeded or error code indicating failure reason. + */ +static uint32_t set_firmware_path(char * p_filename) +{ + uint32_t err_code = NRF_SUCCESS; + + m_tftp_dfu_ctx.p_config_json = cJSON_Parse((const char *)m_tftp_dfu_ctx.config_mem); + if (m_tftp_dfu_ctx.p_config_json == NULL) + { + NRF_LOG_ERROR("JSON parse failed."); + return NRF_ERROR_INVALID_DATA; + } + + switch (m_tftp_dfu_ctx.image_type) + { + case TFTP_DFU_IMAGE_TYPE_APPLICATION: + err_code = get_path(p_filename, CONFIG_APP_KEY); + break; + + case TFTP_DFU_IMAGE_TYPE_BOOTLOADER: + err_code = get_path(p_filename, CONFIG_BL_KEY); + break; + + case TFTP_DFU_IMAGE_TYPE_SOFTDEVICE: + err_code = get_path(p_filename, CONFIG_APPSD_KEY); + break; + + default: + ASSERT(false); + } + + if (p_filename[0] == '\0') + { + NRF_LOG_ERROR("File name has not be found."); + err_code = NRF_ERROR_INVALID_PARAM; + } + + cJSON_Delete(m_tftp_dfu_ctx.p_config_json); + + return err_code; +} + +/** @brief Send download request to the TFTP server. + * + * @param[in] p_file Pointer to file instance used to store file. May be NULL if file is not + * stored by TFTP module. + */ +static void send_request(iot_file_t * p_file) +{ + uint32_t err_code; + iot_tftp_trans_params_t trans_params; + + trans_params.block_size = APP_TFTP_BLOCK_SIZE; + trans_params.next_retr = APP_TFTP_RETRANSMISSION_TIME; + + err_code = iot_tftp_set_params(&m_tftp_dfu_ctx.tftp, &trans_params); + ASSERT(err_code == NRF_SUCCESS); + + err_code = iot_tftp_get(&m_tftp_dfu_ctx.tftp, p_file, m_tftp_dfu_ctx.resource_path); + ASSERT(err_code == NRF_SUCCESS); + + UNUSED_VARIABLE(err_code); +} + +/*************************************************************************************************** + * @section TFTP handler + **************************************************************************************************/ + +/** + * @brief Process retrieved config file + * + * @param[in] p_file Pointer to retrieved config file. + */ +static void config_file_retrieved(iot_file_t * p_file) +{ + uint32_t err_code; + background_dfu_trigger_t trigger; + iot_dfu_firmware_block_t * p_firmware_block = NULL; + + NRF_LOG_INFO("Config file successfully downloaded."); + + m_tftp_dfu_ctx.config_mem[p_file->file_size] = 0; + memset(&m_tftp_dfu_ctx.firmware_desc, 0, sizeof(m_tftp_dfu_ctx.firmware_desc)); + + err_code = app_dfu_config_process(&m_tftp_dfu_ctx.firmware_desc, m_tftp_dfu_ctx.resource_path); + + if (err_code == NRF_SUCCESS) + { + NRF_LOG_INFO("New sofware available. Starting downloading procedure."); + + switch(m_tftp_dfu_ctx.image_type) + { + case TFTP_DFU_IMAGE_TYPE_APPLICATION: + p_firmware_block = &m_tftp_dfu_ctx.firmware_desc.application; + break; + + case TFTP_DFU_IMAGE_TYPE_BOOTLOADER: + p_firmware_block = &m_tftp_dfu_ctx.firmware_desc.bootloader; + break; + + case TFTP_DFU_IMAGE_TYPE_SOFTDEVICE: + p_firmware_block = &m_tftp_dfu_ctx.firmware_desc.softdevice; + break; + } + + memset(&trigger, 0, sizeof(trigger)); + trigger.init_length = uint32_big_decode((const uint8_t *)&p_firmware_block->init_size); + trigger.init_crc = uint32_big_decode((const uint8_t *)&p_firmware_block->init_crc); + trigger.image_length = uint32_big_decode((const uint8_t *)&p_firmware_block->size); + trigger.image_crc = uint32_big_decode((const uint8_t *)&p_firmware_block->crc); + + if (background_dfu_validate_trigger(&m_dfu_ctx, (uint8_t *)&trigger, sizeof(trigger))) + { + if (!background_dfu_process_trigger(&m_dfu_ctx, (uint8_t *)&trigger, sizeof(trigger))) + { + NRF_LOG_ERROR("Error in TFTP background_dfu_process_trigger"); + } + + if (m_dfu_ctx.dfu_state == BACKGROUND_DFU_IDLE) + { + // State in DFU_IDLE, nothing to download. + err_code = iot_tftp_uninit(&m_tftp_dfu_ctx.tftp); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in TFTP uninit (%d)", err_code); + } + } + } + } + else + { + NRF_LOG_INFO("No new sofware available or incorrect JSON file."); + } +} + +/** + * @brief Pass block of retrieved data to background DFU module. + * + * @param[in] p_block Pointer to block of retrieved data. + */ +static void process_block(uint8_t * p_block) +{ + background_dfu_block_t block; + background_dfu_block_result_t result; + + block.number = m_tftp_dfu_ctx.block_number; + block.size = DEFAULT_BLOCK_SIZE; + block.p_payload = p_block; + + m_tftp_dfu_ctx.block_number++; + + result = background_dfu_process_block(&m_dfu_ctx, &block); + if (result != BACKGROUND_DFU_BLOCK_SUCCESS) + { + NRF_LOG_ERROR("Error in TFTP background_dfu_process_block (%d)", result); + } +} + +/** + * @brief Process retrieved data chunk. + * + * Split retrieved data to blocks of DEFAULT_BLOCK_SIZE bytes and pass to DFU module. + * + * @param[in] p_data_received Pointer to structure describing received data chunk. + */ +static void data_chunk_retrieved(iot_tftp_evt_data_received_t * p_data_received) +{ + uint16_t processed_bytes = 0; + + // Process part of block from previous chunk + if (m_tftp_dfu_ctx.block_size) + { + uint16_t first_block_missing_size = DEFAULT_BLOCK_SIZE - m_tftp_dfu_ctx.block_size; + + if (p_data_received->size < first_block_missing_size) + { + first_block_missing_size = p_data_received->size; + } + + memcpy(&m_tftp_dfu_ctx.block[m_tftp_dfu_ctx.block_size], + p_data_received->p_data, + first_block_missing_size); + + process_block(m_tftp_dfu_ctx.block); + + processed_bytes = first_block_missing_size; + } + + // Process received chunks + while (p_data_received->size - processed_bytes >= DEFAULT_BLOCK_SIZE) + { + process_block(&p_data_received->p_data[processed_bytes]); + processed_bytes += DEFAULT_BLOCK_SIZE; + } + + m_tftp_dfu_ctx.block_size = p_data_received->size - processed_bytes; + + // Leave not processed data + if (p_data_received->size > processed_bytes) + { + memcpy(m_tftp_dfu_ctx.block, + &p_data_received->p_data[processed_bytes], + m_tftp_dfu_ctx.block_size); + } +} + +/** + * @brief Process data left from previous chunk. + */ +static void preserved_block_process(void) +{ + process_block(m_tftp_dfu_ctx.block); + m_tftp_dfu_ctx.block_size = 0; +} + +/** + * @brief Handler of TFTP events. + */ +static void tftp_dfu_tftp_handler(iot_tftp_t * p_tftp, iot_tftp_evt_t * p_evt) +{ + switch (p_evt->id) + { + case IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED: + switch (m_dfu_ctx.dfu_state) + { + case BACKGROUND_DFU_DOWNLOAD_TRIG: + break; + + case BACKGROUND_DFU_DOWNLOAD_INIT_CMD: + case BACKGROUND_DFU_DOWNLOAD_FIRMWARE: + data_chunk_retrieved(&p_evt->param.data_received); + break; + + default: + ASSERT(false); + break; + } + break; + + case IOT_TFTP_EVT_TRANSFER_GET_COMPLETE: + switch (m_dfu_ctx.dfu_state) + { + case BACKGROUND_DFU_DOWNLOAD_TRIG: + config_file_retrieved(p_evt->p_file); + break; + + case BACKGROUND_DFU_DOWNLOAD_INIT_CMD: + case BACKGROUND_DFU_DOWNLOAD_FIRMWARE: + preserved_block_process(); + break; + + default: + ASSERT(false); + break; + } + break; + + case IOT_TFTP_EVT_ERROR: + background_dfu_handle_error(); + break; + + default: + ASSERT(false); + break; + } +} + +static void dfu_observer(nrf_dfu_evt_type_t evt_type) +{ + switch (evt_type) + { + case NRF_DFU_EVT_DFU_COMPLETED: + NRF_LOG_FINAL_FLUSH(); + +#if NRF_MODULE_ENABLED(NRF_LOG_BACKEND_RTT) + // To allow the buffer to be flushed by the host. + nrf_delay_ms(100); +#endif + + NVIC_SystemReset(); + break; + + default: + break; + } +} + + +/*************************************************************************************************** + * @section Private API + **************************************************************************************************/ + +void background_dfu_transport_block_request_send(background_dfu_context_t * p_dfu_ctx, + background_dfu_request_bitmap_t * p_req_bmp) +{ + // Intentionally empty: multicast DFU not implemented. +} + +void background_dfu_transport_send_request(background_dfu_context_t * p_dfu_ctx) +{ + switch (m_dfu_ctx.dfu_state) + { + case BACKGROUND_DFU_DOWNLOAD_TRIG: + send_request(&m_tftp_dfu_ctx.config_file); + break; + + default: + // In other states download operation is triggered by state_update() notification. + break; + } +} + +void background_dfu_transport_state_update(background_dfu_context_t * p_dfu_ctx) +{ + switch (p_dfu_ctx->dfu_state) + { + case BACKGROUND_DFU_DOWNLOAD_TRIG: + trigger_path_set(); + break; + + case BACKGROUND_DFU_DOWNLOAD_INIT_CMD: + m_tftp_dfu_ctx.block_number = 0; + m_tftp_dfu_ctx.block_size = 0; + send_request(NULL); + break; + + case BACKGROUND_DFU_DOWNLOAD_FIRMWARE: + m_tftp_dfu_ctx.block_number = 0; + m_tftp_dfu_ctx.block_size = 0; + UNUSED_RETURN_VALUE(set_firmware_path(m_tftp_dfu_ctx.resource_path)); + send_request(NULL); + break; + + case BACKGROUND_DFU_WAIT_FOR_RESET: + // Do nothing. + break; + + default: + NRF_LOG_WARNING("Unhandled state in background_dfu_transport_state_update (s: %s).", + (uint32_t)background_dfu_state_to_string(p_dfu_ctx->dfu_state)); + } +} + +uint32_t background_dfu_random(void) +{ + // Intentionally empty: multicast DFU not implemented. + return 0; +} + +/*************************************************************************************************** + * @section Public API + **************************************************************************************************/ + +uint32_t tftp_dfu_init(void) +{ + uint32_t err_code = NRF_SUCCESS; + + memset(&m_tftp_dfu_ctx, 0, sizeof(m_tftp_dfu_ctx)); + + err_code = nrf_dfu_settings_init(true); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + err_code = nrf_dfu_req_handler_init(dfu_observer); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + background_dfu_state_init(&m_dfu_ctx); + + cJSON_Init(); + + // Initialize file static instance. + IOT_FILE_STATIC_INIT(&m_tftp_dfu_ctx.config_file, + m_tftp_dfu_ctx.resource_path, + m_tftp_dfu_ctx.config_mem, + MAX_CONFIG_SIZE); + + return err_code; +} + +uint32_t tftp_dfu_trigger(const ipv6_addr_t * p_host_ipv6, uint16_t src_port, uint16_t dst_port) +{ + uint32_t err_code; + iot_tftp_init_t tftp_init_params; + + NRF_LOG_INFO("Triggering DFU"); + + if (p_host_ipv6 == NULL) + { + NRF_LOG_WARNING("NULL IPv6 address"); + return NRF_ERROR_NULL; + } + + if (m_dfu_ctx.dfu_state != BACKGROUND_DFU_IDLE) + { + NRF_LOG_WARNING("Invalid state"); + return NRF_ERROR_INVALID_STATE; + } + + // Set TFTP configuration + memset(&tftp_init_params, 0, sizeof(iot_tftp_init_t)); + tftp_init_params.p_ipv6_addr = (ipv6_addr_t *)p_host_ipv6; + tftp_init_params.src_port = src_port; + tftp_init_params.dst_port = dst_port; + tftp_init_params.callback = tftp_dfu_tftp_handler; + + // Initialize instance, bind socket, check parameters. + err_code = iot_tftp_init(&m_tftp_dfu_ctx.tftp, &tftp_init_params); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in TFTP init (%d)", err_code); + return err_code; + } + + trigger_path_set(); + + // Transition from DFU_IDLE to DFU_DOWNLOAD_TRIG. + return background_dfu_handle_event(&m_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE); +} + +void background_dfu_handle_error(void) +{ + UNUSED_RETURN_VALUE(iot_tftp_uninit(&m_tftp_dfu_ctx.tftp)); + tftp_dfu_handle_error(); +} + +__WEAK void tftp_dfu_handle_error(void) +{ + +} + +bool nrf_dfu_button_enter_check(void) +{ + // Dummy function for Keil compilation. This should not be called. + return false; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.h new file mode 100644 index 0000000..6c8370b --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.h @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file tftp_dfu.h + * + * @defgroup tftp_dfu TFTP transport for Background DFU + * @ingroup iot_tftp_dfu + * @{ + * @brief TFTP transport for Background DFU. + * + */ + +#ifndef TFTP_DFU_H_ +#define TFTP_DFU_H_ + +#include <stdint.h> +#include "iot_defines.h" + +/** @brief Initialize DFU client. + * + * @returns NRF_SUCCESS if DFU procedure started. Otherwise an error code indicating problem. + */ +uint32_t tftp_dfu_init(void); + +/** @brief Trigger DFU. + * + * @param[in] p_host_ipv6 IPv6 address of TFTP host. + * @param[in] src_port Source UDP port used by TFTP service. + * @param[in] dst_port Destination UDP port used by TFTP service. + * + * @returns NRF_SUCCESS if DFU procedure started. Otherwise an error code indicating problem. + */ +uint32_t tftp_dfu_trigger(const ipv6_addr_t * p_host_ipv6, uint16_t src_port, uint16_t dst_port); + +/** @brief Handle DFU error. + * + * This function can be implemented in the application to undertake application-specific action on DFU error. + */ +extern void tftp_dfu_handle_error(void); + +#endif /* TFTP_DFU_H_ */ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.c new file mode 100644 index 0000000..b7bf6fa --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.c @@ -0,0 +1,1978 @@ +/** + * Copyright (c) 2013 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup ble_sdk_6lowpan 6LoWPAN Adaptation Layer + * @{ + * @ingroup ble_sdk_iot + * @brief 6LoWPAN Adaptation Layer + * + * @details This module enables 6LoWPAN over Bluetooth Low Energy. + * + */ + +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include "nrf_soc.h" +#include "nordic_common.h" +#include "ble_ipsp.h" +#include "ble_6lowpan.h" +#include "iot_common.h" +#include "iot_context_manager.h" +#include "app_util_platform.h" +#include "mem_manager.h" + +/** + * @defgroup ble_sdk_6lowpan Module's Log Macros + * @details Macros used for creating module logs which can be useful in understanding handling + * of events or actions on API requests. These are intended for debugging purposes and + * can be enabled by defining the IOT_BLE_6LOWPAN_CONFIG_LOG_ENABLED. + * @note If NRF_LOG_ENABLED is disabled, having IOT_BLE_6LOWPAN_CONFIG_LOG_ENABLED + * has no effect. + * @{ + */ + +#if IOT_BLE_6LOWPAN_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME 6lowpan + +#define NRF_LOG_LEVEL IOT_BLE_6LOWPAN_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR IOT_BLE_6LOWPAN_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR IOT_BLE_6LOWPAN_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define BLE_6LOWPAN_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define BLE_6LOWPAN_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define BLE_6LOWPAN_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define BLE_6LOWPAN_ENTRY() BLE_6LOWPAN_TRC(">> %s", __func__) +#define BLE_6LOWPAN_EXIT() BLE_6LOWPAN_TRC("<< %s", __func__) + +#else // IOT_BLE_6LOWPAN_CONFIG_LOG_ENABLED + +#define BLE_6LOWPAN_TRC(...) /**< Disables traces. */ +#define BLE_6LOWPAN_DUMP(...) /**< Disables dumping of octet streams. */ +#define BLE_6LOWPAN_ERR(...) /**< Disables error logs. */ + +#define BLE_6LOWPAN_ENTRY(...) +#define BLE_6LOWPAN_EXIT(...) + +#endif // IOT_BLE_6LOWPAN_CONFIG_LOG_ENABLED + + + +/** @} */ + +/** + * @defgroup ble_6lowpan_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case need arises to use an alternative architecture. + * @{ + */ +#define BLE_6LOWPAN_MUTEX_LOCK() SDK_MUTEX_LOCK(m_6lowpan_mutex) /**< Lock module using mutex */ +#define BLE_6LOWPAN_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_6lowpan_mutex) /**< Unlock module using mutex */ +/** @} */ + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * BLE_6LOWPAN_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks. + * + * @{ + */ + +#if (BLE_6LOWPAN_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Macro to check is module is initialized before requesting one of the module procedures. */ +#define VERIFY_MODULE_IS_INITIALIZED() \ + if (m_event_handler == NULL) \ + { \ + return (SDK_ERR_MODULE_NOT_INITIALIZED | BLE_6LOWPAN_ERR_BASE); \ + } + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | BLE_6LOWPAN_ERR_BASE); \ + } + +/**@brief Check if packet has at least IP Header in it (40 bytes). */ +#define PACKET_LENGTH_CHECK(PARAM) \ + if ((PARAM) < IPV6_IP_HEADER_SIZE) \ + { \ + return (NRF_ERROR_INVALID_PARAM | BLE_6LOWPAN_ERR_BASE); \ + } + +#else // BLE_6LOWPAN_DISABLE_API_PARAM_CHECK + +#define VERIFY_MODULE_IS_INITIALIZED() +#define NULL_PARAM_CHECK(PARAM) +#define PACKET_LENGTH_CHECK(PARAM) + +#endif // BLE_6LOWPAN_DISABLE_API_PARAM_CHECK + +/** @} */ + +/**@brief Maximum different between compressed and uncompressed packet. */ +#define IPHC_MAX_COMPRESSED_DIFF (IPV6_IP_HEADER_SIZE + UDP_HEADER_SIZE - 7) + +/**@brief Transmit FIFO mask. */ +#define TX_FIFO_MASK (BLE_6LOWPAN_TX_FIFO_SIZE - 1) + +/**@brief Value and position of IPHC dispatch. */ +#define IPHC_START_DISPATCH 0x03 +#define IPHC_START_DISPATCH_POS 5 + +/**@brief Values and positions of IPHC fields. */ +#define IPHC_TF_MASK 0x18 +#define IPHC_TF_POS 3 +#define IPHC_NH_MASK 0x04 +#define IPHC_NH_POS 2 +#define IPHC_HLIM_MASK 0x03 +#define IPHC_HLIM_POS 0 +#define IPHC_CID_MASK 0x80 +#define IPHC_CID_POS 7 +#define IPHC_SAC_MASK 0x40 +#define IPHC_SAC_POS 6 +#define IPHC_SAM_MASK 0x30 +#define IPHC_SAM_POS 4 +#define IPHC_M_MASK 0x08 +#define IPHC_M_POS 3 +#define IPHC_DAC_MASK 0x04 +#define IPHC_DAC_POS 2 +#define IPHC_DAM_MASK 0x03 +#define IPHC_DAM_POS 0 + +/**@brief IPHC Traffic Flow compression. */ +#define IPHC_TF_DSCP_MASK 0x3F +#define IPHC_TF_ECN_MASK 0xC0 +#define IPHC_TF_ECN_POS 6 + +/**@brief IPHC values of fields. */ +#define IPHC_TF_00 0x00 +#define IPHC_TF_01 0x01 +#define IPHC_TF_10 0x02 +#define IPHC_TF_11 0x03 +#define IPHC_NH_0 0x00 +#define IPHC_NH_1 0x01 +#define IPHC_HLIM_00 0x00 +#define IPHC_HLIM_01 0x01 +#define IPHC_HLIM_10 0x02 +#define IPHC_HLIM_11 0x03 +#define IPHC_CID_0 0x00 +#define IPHC_CID_1 0x01 +#define IPHC_SAC_0 0x00 +#define IPHC_SAC_1 0x01 +#define IPHC_SAM_00 0x00 +#define IPHC_SAM_01 0x01 +#define IPHC_SAM_10 0x02 +#define IPHC_SAM_11 0x03 +#define IPHC_M_0 0x00 +#define IPHC_M_1 0x01 +#define IPHC_DAC_0 0x00 +#define IPHC_DAC_1 0x01 +#define IPHC_DAM_00 0x00 +#define IPHC_DAM_01 0x01 +#define IPHC_DAM_10 0x02 +#define IPHC_DAM_11 0x03 + +/**@brief IPHC Context Identifier compression. */ +#define IPHC_CID_SOURCE_MASK 0xF0 +#define IPHC_CID_SOURCE_POS 4 +#define IPHC_CID_DESTINATION_MASK 0x0F +#define IPHC_CID_DESTINATION_POS 0 + +/**@brief IPHC Next Header Compression dispatches. */ +#define IPHC_NHC_UDP_DISPATCH 0xF0 +#define IPHC_NHC_UDP_MASK 0xF8 +#define IPHC_NHC_EXT_DISPATCH 0xE0 +#define IPHC_NHC_EXT_MASK 0xF0 + +/**@brief IPHC Next Header Compression UDP fields. */ +#define IPHC_NHC_UDP_CSUM_MASK 0x04 +#define IPHC_NHC_UDP_CSUM_POS 0x02 +#define IPHC_NHC_UDP_PORTS_MASK 0x03 +#define IPHC_NHC_UDP_PORTS_POS 0x00 +#define IPHC_NHC_UDP_PORTS_00 0x00 +#define IPHC_NHC_UDP_PORTS_01 0x01 +#define IPHC_NHC_UDP_PORTS_10 0x02 +#define IPHC_NHC_UDP_PORTS_11 0x03 + +#define IPHC_NHC_UDP_COMPRESSION_MAX_MASK 0xFFF0 +#define IPHC_NHC_UDP_COMPRESSION_MAX 0xF0B0 +#define IPHC_NHC_UDP_COMPRESSION_MIN_MASK 0xFF00 +#define IPHC_NHC_UDP_COMPRESSION_MIN 0xF000 + +/**@brief IPHC Next Header Compression Extended Header fields. */ +#define IPHC_NHC_EXT_EID_MASK 0x0E +#define IPHC_NHC_EXT_EID_POS 0x01 +#define IPHC_NHC_EXT_EID_HOP_BY_HOP 0x00 +#define IPHC_NHC_EXT_EID_ROUTING 0x01 +#define IPHC_NHC_EXT_EID_FRAGMENT 0x02 +#define IPHC_NHC_EXT_EID_DESTINATION 0x03 +#define IPHC_NHC_EXT_EID_MOBILITY 0x04 +#define IPHC_NHC_EXT_EID_IPV6 0x07 + +/**@brief IPHC default value of IPv6 Header fields. */ +#define IPHC_IPHEADER_VER_TC 0x60 +#define IPHC_IPHEADER_TC_FL 0x00 +#define IPHC_IPHEADER_FL 0x00 + +/**@brief Check if address can be fully elidable. */ +#define IPV6_ADDRESS_IS_FULLY_ELIDABLE(ll_addr, addr) \ + (((addr)->u8[8] == (((ll_addr[0]) ^ IPV6_IID_FLIP_VALUE))) && \ + ((addr)->u8[9] == ll_addr[1]) && \ + ((addr)->u8[10] == ll_addr[2]) && \ + ((addr)->u8[11] == ll_addr[3]) && \ + ((addr)->u8[12] == ll_addr[4]) && \ + ((addr)->u8[11] == 0xff) && \ + ((addr)->u8[12] == 0xfe) && \ + ((addr)->u8[13] == ll_addr[5]) && \ + ((addr)->u8[14] == ll_addr[6]) && \ + ((addr)->u8[15] == ll_addr[7]) \ + ) + +/**@brief Check if address is 16-bit and can be compressed. + * 16-bit COMPRESSABLE format: ::0000:00ff:fe00:XXXX. + */ +#define IPV6_ADDRESS_IS_16_BIT_COMPRESSABLE(addr) \ + (((addr)->u8[8] == 0) && \ + ((addr)->u8[9] == 0) && \ + ((addr)->u8[10] == 0) && \ + ((addr)->u8[11] == 0xff) && \ + ((addr)->u8[12] == 0xfe) && \ + ((addr)->u8[13] == 0) \ + ) + +/**@brief Check if address is 48-bit multi-cast and can be compressed. + * 48-bit COMPRESSABLE format: FFXX::00XX:XXXX:XXXX. + */ +#define IPV6_ADDRESS_IS_48_BIT_MCAST_COMPRESSABLE(addr) \ + (((addr)->u16[1] == 0) && \ + ((addr)->u16[2] == 0) && \ + ((addr)->u16[3] == 0) && \ + ((addr)->u16[4] == 0) && \ + ((addr)->u8[10] == 0) \ + ) + +/**@brief Check if address is 32-bit multi-cast and can be compressed. + * 32-bit COMPRESSABLE format: FFXX::00XX:XXXX. + */ +#define IPV6_ADDRESS_IS_32_BIT_MCAST_COMPRESSABLE(addr) \ + (((addr)->u16[1] == 0) && \ + ((addr)->u32[1] == 0) && \ + ((addr)->u32[2] == 0) && \ + ((addr)->u8[12] == 0) \ + ) + +/**@brief Check if address is 8-bit multi-cast and can be compressed. + * 8-bit COMPRESSABLE format: FF02::XX. + */ +#define IPV6_ADDRESS_IS_8_BIT_MCAST_COMPRESSABLE(addr) \ + (((addr)->u8[1] == 2) && \ + ((addr)->u16[1] == 0) && \ + ((addr)->u32[1] == 0) && \ + ((addr)->u32[2] == 0) && \ + ((addr)->u16[6] == 0) && \ + ((addr)->u8[14] == 0) \ + ) + +/****************************************************************************** + * 6LoWPAN Core and Transport structures. + ******************************************************************************/ + +/**@brief Element of TX Queue. */ +typedef struct +{ + uint8_t * p_mem_block; /**< Base address of memory block, using for release the buffer. */ + uint8_t * p_data; /**< Pointer to TX Data. */ + uint16_t data_len; /**< Size of TX data. */ +} tx_packet_t; + +/**@brief A TX Queue (FIFO) structure. */ +typedef struct +{ + tx_packet_t packets[BLE_6LOWPAN_TX_FIFO_SIZE]; /**< Array of TX packet in FIFO. */ + volatile uint32_t read_pos; /**< Next read position in the FIFO buffer. */ + volatile uint32_t write_pos; /**< Next write position in the FIFO buffer. */ +} tx_fifo_t; + +/**@brief Transport instance structure. */ +typedef struct +{ + iot_interface_t interface; + ble_ipsp_handle_t handle; + tx_fifo_t tx_fifo; + tx_packet_t * p_tx_cur_packet; +} transport_instance_t; + +/****************************************************************************** + * @name Global variables + *****************************************************************************/ + +/**@brief Application Event Handler. */ +static ble_6lowpan_evt_handler_t m_event_handler = NULL; + +/**@brief Hop Limit options. */ +static const uint8_t m_hop_limit[] = {0, 1, 64, 255}; + +/**@brief Link-local prefix. */ +static const uint8_t m_link_local_prefix[] = {0xFE, 0x80}; + +/**@brief Additional extenders for EUI-48. */ +static const uint8_t m_link_local_16_middle[] = {0xFF, 0xFE}; + +/**@brief nRF EUI-64 link-layer address */ +static eui64_t m_ll_addr = {{0, 0, 0, 0, 0, 0, 0, 0}}; + +/**@brief Transport interfaces table. */ +static transport_instance_t m_instances[BLE_6LOWPAN_MAX_INTERFACE]; + +/**@brief Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ +SDK_MUTEX_DEFINE(m_6lowpan_mutex) + +/****************************************************************************** + * @name 6LoWPAN core functions + *****************************************************************************/ + +/**@brief Function for checking if IID can be completely elided. This situation + * may happen when receiver can still reconstruct IPv6 address by using context + * prefix and Link Layer address. + * + * @param[in] p_addr Pointer to IPv6 address. + * @param[in] p_context Pointer to context entry that is compressed with. + * @param[in] p_ll_addr Pointer to link layer address of BT-LE device. + * + * @return True if IID can be elided, False otherwise. + */ +static bool is_context_cover_iid(const ipv6_addr_t * p_addr, + const iot_context_t * p_context, + const eui64_t * p_ll_addr) +{ + uint32_t start_byte, offset; + + // Context covers IPv6 address by its size. + if (p_context->prefix_len == 128) + { + return true; + } + + // Check if IID can be retrieved in case of longer prefix than 64 bits. + if (p_context->prefix_len > 64) + { + // Check only IID fields that are not covered by context prefix. + start_byte = p_context->prefix_len >> 3; + offset = p_context->prefix_len % 8; + + // Check all bytes from the second one. + if (start_byte == 15 || + 0 == memcmp(&p_addr->u8[start_byte+1], &p_ll_addr->identifier[start_byte-7], 15-start_byte)) + { + // Then check first byte. + return (p_addr->u8[start_byte] << offset) == (p_ll_addr->identifier[start_byte-8] << offset); + } + } + + return false; +} + +/**@brief Function for decoding Next Header Compression. + * It supports UDP header decompression. + * + * @param[in] p_iphc Pointer to currently process IPHC field. + * @param[in] p_data Pointer to constructing uncompressed IP packet. + * @param[in] p_length Place of UDP header in case of Extension Header. + * @param[out] p_length Length of the constructed uncompressed header. + * + * @return Number of processed IPHC field. + */ +static uint32_t iphc_nhc_decode(uint8_t * p_iphc, uint8_t * p_data, uint16_t * p_length) +{ + uint8_t nhc_dispatch = *p_iphc; + uint8_t * p_nhc = p_iphc; + + ipv6_header_t * iphdr = (ipv6_header_t *)&p_data[0]; + + // UDP Next Header Compression. + if ((nhc_dispatch & IPHC_NHC_UDP_MASK) == IPHC_NHC_UDP_DISPATCH) + { + udp6_header_t * udphdr = (udp6_header_t * )&p_data[IPV6_IP_HEADER_SIZE + *p_length]; + + iphdr->next_header = IPV6_NEXT_HEADER_UDP; + + // Start length from UDP Header Size. + *p_length += UDP_HEADER_SIZE; + p_nhc += 1; + + switch ((nhc_dispatch & IPHC_NHC_UDP_PORTS_MASK) >> IPHC_NHC_UDP_PORTS_POS) + { + case IPHC_NHC_UDP_PORTS_00: + memcpy(&udphdr->srcport, p_nhc, 2); + memcpy(&udphdr->destport, p_nhc + 2, 2); + p_nhc += 4; + break; + + case IPHC_NHC_UDP_PORTS_01: + memcpy(&udphdr->srcport, p_nhc, 2); + udphdr->destport = HTONS(IPHC_NHC_UDP_COMPRESSION_MIN | *(p_nhc + 2)); + p_nhc += 3; + break; + + case IPHC_NHC_UDP_PORTS_10: + udphdr->srcport = HTONS(IPHC_NHC_UDP_COMPRESSION_MIN | *p_nhc); + memcpy(&udphdr->destport, p_nhc + 1, 2); + p_nhc += 3; + break; + + case IPHC_NHC_UDP_PORTS_11: + udphdr->srcport = HTONS((IPHC_NHC_UDP_COMPRESSION_MAX | ((*p_nhc & 0xf0) >> 4))); + udphdr->destport = HTONS((IPHC_NHC_UDP_COMPRESSION_MAX | ((*p_nhc & 0x0f)))); + p_nhc += 1; + break; + } + + if ((nhc_dispatch & IPHC_NHC_UDP_CSUM_MASK) >> IPHC_NHC_UDP_CSUM_POS) + { + udphdr->checksum = 0; + } + else + { + memcpy(&udphdr->checksum, p_nhc, 2); + p_nhc += 2; + } + } + + return (p_nhc - p_iphc); +} + +/**@brief Function for encoding Next Header Compression. + * It supports UDP header compression. + * + * @param[in] p_iphc Pointer to currently process IPHC field. + * @param[in] p_data Pointer to constructing uncompressed IP packet. + * @param[in] p_length Place of UDP header in case of Extension Header. + * @param[out] p_length Length of the constructed uncompressed header. + * + * @return Number of processed IPHC field. + */ +static uint32_t iphc_nhc_encode(uint8_t * p_iphc, const uint8_t * p_data, uint16_t * p_length) +{ + uint8_t * p_nhc = p_iphc; + ipv6_header_t * iphdr = (ipv6_header_t *)p_data; + + switch (iphdr->next_header) + { + case IPV6_NEXT_HEADER_UDP: + { + udp6_header_t * udphdr = (udp6_header_t * )&p_data[IPV6_IP_HEADER_SIZE + *p_length]; + *p_iphc = IPHC_NHC_UDP_DISPATCH; + p_nhc += 1; + + // Full 4-bit compression for source and destination ports. + if ( ((HTONS(udphdr->srcport) & IPHC_NHC_UDP_COMPRESSION_MAX_MASK) == + IPHC_NHC_UDP_COMPRESSION_MAX) && + ((HTONS(udphdr->destport) & IPHC_NHC_UDP_COMPRESSION_MAX_MASK) == + IPHC_NHC_UDP_COMPRESSION_MAX)) + { + *p_iphc |= (IPHC_NHC_UDP_PORTS_11 >> IPHC_NHC_UDP_PORTS_POS); + + *p_nhc = + (((HTONS(udphdr->srcport) & 0x0f) << 4) | (HTONS(udphdr->destport) & 0x0f)); + p_nhc += 1; + } + // Source port compressed, destination in-line. + else if ((HTONS(udphdr->srcport) & IPHC_NHC_UDP_COMPRESSION_MIN_MASK) == + IPHC_NHC_UDP_COMPRESSION_MIN) + { + *p_iphc |= (IPHC_NHC_UDP_PORTS_10 >> IPHC_NHC_UDP_PORTS_POS); + + *p_nhc = (HTONS(udphdr->srcport) & 0xff); + p_nhc += 1; + + memcpy(p_nhc, &udphdr->destport, 2); + p_nhc += 2; + } + // Source port in-line, destination compressed. + else if ((HTONS(udphdr->destport) & IPHC_NHC_UDP_COMPRESSION_MIN_MASK) == + IPHC_NHC_UDP_COMPRESSION_MIN) + { + *p_iphc |= (IPHC_NHC_UDP_PORTS_01 >> IPHC_NHC_UDP_PORTS_POS); + + memcpy(p_nhc, &udphdr->srcport, 2); + p_nhc += 2; + + *p_nhc = (HTONS(udphdr->destport) & 0xff); + p_nhc += 1; + } + // Source and destination port in-line. + else + { + *p_iphc |= (IPHC_NHC_UDP_PORTS_00 >> IPHC_NHC_UDP_PORTS_POS); + + memcpy(p_nhc, &udphdr->srcport, 2); + memcpy(p_nhc + 2, &udphdr->destport, 2); + p_nhc += 4; + } + + // Checksum always in-line, [RFC4944] disallows the compression of the + // UDP checksum. The compressor MUST NOT set the C bit unless it has received + // authorization. + memcpy(p_nhc, &udphdr->checksum, 2); + p_nhc += 2; + + // Set UDP ext header size. + *p_length = UDP_HEADER_SIZE; + + break; + } + } + + return (p_nhc - p_iphc); +} + + + /**@brief Function for checking if it's possible to use NHC. + * + * @param[in] next_header Value of Next Header field in IPv6 packet. + * + * @return Returns 1 if header can be compressed, otherwise 0. + */ +static uint32_t iphc_nhc_compressable(uint8_t next_header) +{ + switch (next_header) + { + case IPV6_NEXT_HEADER_UDP: + return 1; + } + + return 0; +} + + +/**@brief Function for decoding IPHC (IP Header Compression) defined in + * IETF RFC 6282. + * + * @param[in] p_instance Transport instance from where packet came. + * @param[in] p_input Pointer to received packet from IPSP module. + * @param[in] input_len Length of received packet. + * @param[in] p_output Pointer to allocated buffer for decompressed packet. + * @param[out] p_output Pointer to decompressed IPv6 packet. + * @param[out] p_output_len Length of decompressed IPv6 packet. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t iphc_decode(iot_interface_t * p_interface, + uint8_t * p_output, + uint16_t * p_output_len, + uint8_t * p_input, + uint16_t input_len, + iot_context_id_t * p_rx_contexts) +{ + uint32_t retval = NRF_SUCCESS; + uint32_t err_code = NRF_SUCCESS; + uint8_t * p_iphc = p_input; + uint8_t sci = IPV6_CONTEXT_IDENTIFIER_NONE; + uint8_t dci = IPV6_CONTEXT_IDENTIFIER_NONE; + uint16_t nhc_length = 0; + iot_context_t * p_ctx = NULL; + + // IPv6 headers used in decompression. + ipv6_header_t * p_iphdr = (ipv6_header_t *)p_output; + udp6_header_t * p_udphdr = (udp6_header_t *)&p_output[IPV6_IP_HEADER_SIZE]; + + // Check if format of packet is correct. + if ((p_input[0] >> IPHC_START_DISPATCH_POS) != IPHC_START_DISPATCH) + { + BLE_6LOWPAN_ERR("[6LoWPAN] Packet has incorrect IPHC structure!"); + + return NRF_ERROR_INVALID_DATA; + } + + // IPHC basic form has 2 bytes. + p_iphc += 2; + + // RFC6282: An additional 8-bit Context Identifier Extension field + // immediately follows the Destination Address Mode (DAM) field. + if ((p_input[1] & IPHC_CID_MASK)) + { + sci = ((*p_iphc & IPHC_CID_SOURCE_MASK) >> IPHC_CID_SOURCE_POS); + dci = ((*p_iphc & IPHC_CID_DESTINATION_MASK) >> IPHC_CID_DESTINATION_POS); + p_iphc += 1; + } + + // Set proper context identifiers. + p_rx_contexts->src_cntxt_id = sci; + p_rx_contexts->dest_cntxt_id = dci; + + switch ((p_input[0] & IPHC_TF_MASK) >> IPHC_TF_POS) + { + case IPHC_TF_11: + // Elide Traffic Class and Flow Label. + p_iphdr->version_traffic_class = IPHC_IPHEADER_VER_TC; + p_iphdr->traffic_class_flowlabel = IPHC_IPHEADER_TC_FL; + p_iphdr->flowlabel = IPHC_IPHEADER_FL; + break; + + case IPHC_TF_10: + // Elide Flow Label. + p_iphdr->version_traffic_class = IPHC_IPHEADER_VER_TC | ((*p_iphc & IPHC_TF_DSCP_MASK) >> 2); + p_iphdr->traffic_class_flowlabel = ((*p_iphc & 0x03) << 6) | + ((*p_iphc & IPHC_TF_ECN_MASK) >> 2); + p_iphdr->flowlabel = IPHC_IPHEADER_FL; + p_iphc += 1; + break; + + case IPHC_TF_01: + // Elide DSCP, carry ECN and Flow Label. + p_iphdr->version_traffic_class = IPHC_IPHEADER_VER_TC; + p_iphdr->traffic_class_flowlabel = ((*p_iphc & IPHC_TF_ECN_MASK) >> 2) | + ((*p_iphc & 0x0f)); + memcpy(&p_iphdr->flowlabel, p_iphc + 1, 2); + p_iphc += 3; + break; + + case IPHC_TF_00: + // Flow Label and Traffic Class in-line. + p_iphdr->version_traffic_class = IPHC_IPHEADER_VER_TC | ((*p_iphc & IPHC_TF_DSCP_MASK) >> 2); + p_iphdr->traffic_class_flowlabel = ((*p_iphc & 0x03) << 6) | + ((*p_iphc & IPHC_TF_ECN_MASK) >> 2) | + ((*(p_iphc + 1) & 0x0f)); + memcpy(&p_iphdr->flowlabel, p_iphc + 2, 2); + p_iphc += 4; + break; + } + + if (!((p_input[0] & IPHC_NH_MASK))) + { + // Set next header. + p_iphdr->next_header = *p_iphc++; + } + + if ((p_input[0] & IPHC_HLIM_MASK)) + { + p_iphdr->hoplimit = m_hop_limit[((p_input[0] & IPHC_HLIM_MASK) >> IPHC_HLIM_POS)]; + } + else + { + p_iphdr->hoplimit = *p_iphc++; + } + + // Clear IPv6 addresses. + memset(p_iphdr->srcaddr.u8, 0, IPV6_ADDR_SIZE); + memset(p_iphdr->destaddr.u8, 0, IPV6_ADDR_SIZE); + + // Source address decompression. + if ((p_input[1] & IPHC_SAC_MASK) >> IPHC_SAC_POS) + { + if ( ((p_input[1] & IPHC_SAM_MASK) >> IPHC_SAM_POS) == IPHC_SAM_00 ) + { + // Unspecified address. + memset(p_iphdr->srcaddr.u8, 0, IPV6_ADDR_SIZE); + } + else + { + switch ((p_input[1] & IPHC_SAM_MASK) >> IPHC_SAM_POS) + { + case IPHC_SAM_01: + // 64 bits in-line, first IID then prefix in case prefix > 64. + memcpy(p_iphdr->srcaddr.u8 + 8, p_iphc, 8); + p_iphc += 8; + break; + + case IPHC_SAM_10: + // 16 bits in-line. + memcpy(p_iphdr->srcaddr.u8 + 14, p_iphc, 2); + memcpy(p_iphdr->srcaddr.u8 + 11, m_link_local_16_middle, 2); + p_iphc += 2; + break; + + case IPHC_SAM_11: + // Take the address from lower layer. + memcpy(p_iphdr->srcaddr.u8 + 8, p_interface->peer_addr.identifier, 8); + p_iphdr->srcaddr.u8[8] ^= IPV6_IID_FLIP_VALUE; + break; + } + + /* Look up for context */ + BLE_6LOWPAN_MUTEX_UNLOCK(); + err_code = iot_context_manager_get_by_cid(p_interface, sci, &p_ctx); + BLE_6LOWPAN_MUTEX_LOCK(); + + if (err_code == NRF_SUCCESS) + { + /* Add prefix */ + IPV6_ADDRESS_PREFIX_SET(p_iphdr->srcaddr.u8, p_ctx->prefix.u8, p_ctx->prefix_len); + } + else + { + /* Set error and continue decompression. */ + retval = BLE_6LOWPAN_CID_NOT_FOUND; + + BLE_6LOWPAN_ERR("Cannot find context identifier in the context table."); + } + } + } + else + { + switch ((p_input[1] & IPHC_SAM_MASK) >> IPHC_SAM_POS) + { + case IPHC_SAM_00: + // Full 128-bits in-line. + memcpy(p_iphdr->srcaddr.u8, p_iphc, IPV6_ADDR_SIZE); + p_iphc += IPV6_ADDR_SIZE; + break; + + case IPHC_SAM_01: + // 64 bits in-line, first IID then prefix in case prefix > 64. + memcpy(p_iphdr->srcaddr.u8, m_link_local_prefix, 2); + memcpy(p_iphdr->srcaddr.u8 + 8, p_iphc, 8); + p_iphc += 8; + break; + + case IPHC_SAM_10: + // 16 bits in-line. + memcpy(p_iphdr->srcaddr.u8, m_link_local_prefix, 2); + memcpy(p_iphdr->srcaddr.u8 + 11, m_link_local_16_middle, 2); + memcpy(p_iphdr->srcaddr.u8 + 14, p_iphc, 2); + p_iphc += 2; + break; + + case IPHC_SAM_11: + memcpy(p_iphdr->srcaddr.u8, m_link_local_prefix, 2); + memcpy(p_iphdr->srcaddr.u8 + 8, p_interface->peer_addr.identifier, 8); + p_iphdr->srcaddr.u8[8] ^= IPV6_IID_FLIP_VALUE; + break; + } + } + + // Destination address decompression. + if ((p_input[1] & IPHC_DAC_MASK) >> IPHC_DAC_POS) + { + if ((p_input[1] & IPHC_M_MASK) >> IPHC_M_POS) + { + switch ((p_input[1] & IPHC_DAM_MASK) >> IPHC_DAM_POS) + { + case IPHC_DAM_00: + // 48-bits in-line. + p_iphdr->destaddr.u8[0] = 0xff; + memcpy(p_iphdr->destaddr.u8 + 1, p_iphc, 2); + memcpy(p_iphdr->destaddr.u8 + 12, p_iphc + 2, 4); + p_iphc += 6; + break; + + default: + BLE_6LOWPAN_ERR("Reserved value in IPHC header!"); + return NRF_ERROR_INVALID_DATA; + } + } + else + { + switch ((p_input[1] & IPHC_DAM_MASK) >> IPHC_DAM_POS) + { + case IPHC_DAM_01: + // 64 bits in-line. + memcpy(p_iphdr->destaddr.u8 + 8, p_iphc, 8); + p_iphc += 8; + break; + + case IPHC_DAM_10: + // 16 bits in-line. + memcpy(p_iphdr->destaddr.u8 + 11, m_link_local_16_middle, 2); + memcpy(p_iphdr->destaddr.u8 + 14, p_iphc, 2); + p_iphc += 2; + break; + + case IPHC_DAM_11: + // Take the address from lower layer. + memcpy(p_iphdr->destaddr.u8 + 8, p_interface->local_addr.identifier, 8); + p_iphdr->destaddr.u8[8] ^= IPV6_IID_FLIP_VALUE; + break; + + default: + BLE_6LOWPAN_ERR("Reserved value in IPHC header!"); + return NRF_ERROR_INVALID_DATA; + } + + /* Look up for context */ + BLE_6LOWPAN_MUTEX_UNLOCK(); + err_code = iot_context_manager_get_by_cid(p_interface, dci, &p_ctx); + BLE_6LOWPAN_MUTEX_LOCK(); + + if (err_code == NRF_SUCCESS) + { + /* Add prefix */ + IPV6_ADDRESS_PREFIX_SET(p_iphdr->destaddr.u8, p_ctx->prefix.u8, p_ctx->prefix_len); + } + else + { + /* Set error and continue decompression. */ + retval = BLE_6LOWPAN_CID_NOT_FOUND; + + BLE_6LOWPAN_ERR("Cannot find context identifier in the context table."); + } + } + } + else + { + if ((p_input[1] & IPHC_M_MASK) >> IPHC_M_POS) + { + switch ((p_input[1] & IPHC_DAM_MASK) >> IPHC_DAM_POS) + { + case IPHC_DAM_00: + // 128 bits in-line. + memcpy(p_iphdr->destaddr.u8, p_iphc, IPV6_ADDR_SIZE); + p_iphc += IPV6_ADDR_SIZE; + break; + + case IPHC_DAM_01: + // 48 bits in-line. + p_iphdr->destaddr.u8[0] = 0xFF; + p_iphdr->destaddr.u8[1] = *p_iphc; + memcpy(p_iphdr->destaddr.u8 + 11, p_iphc + 1, 5); + p_iphc += 6; + break; + + case IPHC_DAM_10: + // 32 bits in-line. + p_iphdr->destaddr.u8[0] = 0xFF; + p_iphdr->destaddr.u8[1] = *p_iphc; + memcpy(p_iphdr->destaddr.u8 + 13, p_iphc + 1, 3); + p_iphc += 4; + break; + + case IPHC_DAM_11: + // 8 bits in-line. + p_iphdr->destaddr.u8[0] = 0xFF; + p_iphdr->destaddr.u8[1] = 0x02; + p_iphdr->destaddr.u8[15] = *p_iphc; + p_iphc += 1; + break; + } + } + else + { + switch ((p_input[1] & IPHC_DAM_MASK) >> IPHC_DAM_POS) + { + case IPHC_DAM_00: + // 128 bits in-line. + memcpy(p_iphdr->destaddr.u8, p_iphc, IPV6_ADDR_SIZE); + p_iphc += IPV6_ADDR_SIZE; + break; + + case IPHC_DAM_01: + // 64 bits in-line, first IID then prefix in case prefix > 64. + memcpy(p_iphdr->destaddr.u8, m_link_local_prefix, 2); + memcpy(p_iphdr->destaddr.u8 + 8, p_iphc, 8); + p_iphc += 8; + break; + + case IPHC_DAM_10: + // 16 bits in-line. + memcpy(p_iphdr->destaddr.u8, m_link_local_prefix, 2); + memcpy(p_iphdr->destaddr.u8 + 11, m_link_local_16_middle, 2); + memcpy(p_iphdr->destaddr.u8 + 14, p_iphc, 2); + p_iphc += 2; + break; + + case IPHC_DAM_11: + // Take the address from lower layer. + memcpy(p_iphdr->destaddr.u8, m_link_local_prefix, 2); + memcpy(p_iphdr->destaddr.u8 + 8, p_interface->local_addr.identifier, 8); + p_iphdr->destaddr.u8[8] ^= IPV6_IID_FLIP_VALUE; + break; + } + } + } + + // Perform next header compression. + if (((p_input[0] & IPHC_NH_MASK) >> IPHC_NH_POS)) + { + p_iphc += iphc_nhc_decode(p_iphc, p_output, &nhc_length); + + if (nhc_length == 0) + { + // Unknown NHC field. + BLE_6LOWPAN_ERR("IPHC contains unsupported NHC header!"); + + return NRF_ERROR_INVALID_DATA; + } + } + + // Calculate IPv6 Header Length. + p_iphdr->length = input_len - (p_iphc - p_input); + + // Check if IPHC contains more bytes than whole packet. + if (p_iphdr->length > input_len) + { + // We cannot decompress it. + BLE_6LOWPAN_ERR("IPHC contains more bytes than expected!"); + + return NRF_ERROR_INVALID_DATA; + } + + // Copy the data. + memcpy(p_output + IPV6_IP_HEADER_SIZE + nhc_length, p_iphc, p_iphdr->length); + + // Add uncompressed headers length if any. + p_iphdr->length += nhc_length; + + // Return length of whole IPv6 packet. + *p_output_len = p_iphdr->length + IPV6_IP_HEADER_SIZE; + + // Keep proper endianness. + p_iphdr->length = HTONS(p_iphdr->length); + + // Restore UDP length if compression was used. + if ( p_iphdr->next_header == IPV6_NEXT_HEADER_UDP ) + { + memcpy(&p_udphdr->length, &p_iphdr->length, 2); + } + + return retval; +} + + +/**@brief Function for encoding IPHC (IP Header Compression) defined in + * IETF RFC 6282. Instead of having separate buffer for compression, + * needed compression is performed on the IPv6 packet and buffer holding + * the packet is reused to overwrite the headers compressed. + * + * @param[in] p_interface IoT interface where packet must be sent. + * @param[in] p_input Pointer to full IPv6 packet. + * @param[in] input_len Length of IPv6 packet. + * @param[out] p_output Pointer to place of start IPHC packet. + * @param[out] p_output_len Length of compressed packet. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t iphc_encode(const iot_interface_t * p_interface, + uint8_t ** p_output, + uint16_t * p_output_len, + const uint8_t * p_input, + uint16_t input_len) +{ + // Create a buffer with maximum of IPHC value. + uint32_t err_code; + uint8_t iphc_buff[IPV6_IP_HEADER_SIZE + UDP_HEADER_SIZE]; + uint8_t traffic_class; + uint8_t * p_iphc = &iphc_buff[2]; + uint16_t iphc_len = 0; + uint16_t nhc_length = 0; + iot_context_t * p_ctx = NULL; + uint8_t sci = p_interface->tx_contexts.src_cntxt_id; + uint8_t dci = p_interface->tx_contexts.dest_cntxt_id; + bool sci_cover = false; + bool dci_cover = false; + + // IPv6 header. + ipv6_header_t * p_iphdr = (ipv6_header_t *)p_input; + + *p_iphc = 0; + + // Set IPHC dispatch. + iphc_buff[0] = IPHC_START_DISPATCH << IPHC_START_DISPATCH_POS; + + // Check if address can be compressed using context identifier. + if (sci == IPV6_CONTEXT_IDENTIFIER_NONE) + { + BLE_6LOWPAN_MUTEX_UNLOCK(); + err_code = iot_context_manager_get_by_addr(p_interface, &p_iphdr->srcaddr, &p_ctx); + BLE_6LOWPAN_MUTEX_LOCK(); + + if (err_code == NRF_SUCCESS) + { + sci_cover = is_context_cover_iid(&p_iphdr->srcaddr, p_ctx, &p_interface->local_addr); + sci = p_ctx->context_id; + } + } + + if (dci == IPV6_CONTEXT_IDENTIFIER_NONE) + { + BLE_6LOWPAN_MUTEX_UNLOCK(); + err_code = iot_context_manager_get_by_addr(p_interface, &p_iphdr->destaddr, &p_ctx); + BLE_6LOWPAN_MUTEX_LOCK(); + + if (err_code == NRF_SUCCESS) + { + dci_cover = is_context_cover_iid(&p_iphdr->destaddr, p_ctx, &p_interface->peer_addr); + dci = p_ctx->context_id; + } + } + + if (((sci != IPV6_CONTEXT_IDENTIFIER_NONE) || dci != IPV6_CONTEXT_IDENTIFIER_NONE)) + { + iphc_buff[1] = (IPHC_CID_1 << IPHC_CID_POS); + + // Add Source Context if exists. + if (sci != IPV6_CONTEXT_IDENTIFIER_NONE) + { + *p_iphc |= (sci << 4); + } + + // Add Destination Context if exists. + if (dci != IPV6_CONTEXT_IDENTIFIER_NONE) + { + *p_iphc |= dci; + } + + p_iphc += 1; + } + else + { + // Unset Context Identifier bit. + iphc_buff[1] = (IPHC_CID_0 << IPHC_CID_POS); + } + + // Change ECN with DSCP in Traffic Class. + traffic_class = (p_iphdr->version_traffic_class & 0x0f) << 4; + traffic_class |= ((p_iphdr->traffic_class_flowlabel & 0xf0) >> 4); + traffic_class = (((traffic_class & 0x03) << 6) | (traffic_class >> 2)); + + if ((p_iphdr->flowlabel == 0) && ((p_iphdr->traffic_class_flowlabel & 0x0f) == 0)) + { + if (traffic_class == 0) + { + // Elide Flow Label and Traffic Class. + iphc_buff[0] |= (IPHC_TF_11 << IPHC_TF_POS); + } + else + { + // Elide Flow Label and carry Traffic Class in-line. + iphc_buff[0] |= (IPHC_TF_10 << IPHC_TF_POS); + + *p_iphc++ = traffic_class; + } + } + else + { + if (traffic_class & IPHC_TF_DSCP_MASK) + { + // Carry Flow Label and Traffic Class in-line. + iphc_buff[0] |= (IPHC_TF_00 << IPHC_TF_POS); + + *p_iphc++ = traffic_class; + *p_iphc++ = (p_iphdr->traffic_class_flowlabel & 0x0f); + memcpy(p_iphc, &p_iphdr->flowlabel, 2); + p_iphc += 2; + } + else + { + // Carry Flow Label and ECN only with 2-bit padding. + iphc_buff[0] |= (IPHC_TF_01 << IPHC_TF_POS); + + *p_iphc++ = + ((traffic_class & IPHC_TF_ECN_MASK) | (p_iphdr->traffic_class_flowlabel & 0x0f)); + memcpy(p_iphc, &p_iphdr->flowlabel, 2); + p_iphc += 2; + } + } + + // Checks if next header is compressed. + if (iphc_nhc_compressable(p_iphdr->next_header)) + { + iphc_buff[0] |= (IPHC_NH_1 << IPHC_NH_POS); + } + else + { + iphc_buff[0] |= (IPHC_NH_0 << IPHC_NH_POS); + *p_iphc++ = p_iphdr->next_header; + } + + // Hop limit compression. + switch (p_iphdr->hoplimit) + { + case 1: + iphc_buff[0] |= (IPHC_HLIM_01 << IPHC_HLIM_POS); + break; + + case 64: + iphc_buff[0] |= (IPHC_HLIM_10 << IPHC_HLIM_POS); + break; + + case 255: + iphc_buff[0] |= (IPHC_HLIM_11 << IPHC_HLIM_POS); + break; + + default: + // Carry Hop Limit in-line. + iphc_buff[0] |= (IPHC_HLIM_00 << IPHC_HLIM_POS); + *p_iphc++ = p_iphdr->hoplimit; + break; + } + + // Source address compression. + if (IPV6_ADDRESS_IS_UNSPECIFIED(&p_iphdr->srcaddr)) + { + iphc_buff[1] |= (IPHC_SAC_1 << IPHC_SAC_POS); + iphc_buff[1] |= (IPHC_SAM_00 << IPHC_SAM_POS); + } + else if (sci != IPV6_CONTEXT_IDENTIFIER_NONE || IPV6_ADDRESS_IS_LINK_LOCAL(&p_iphdr->srcaddr)) + { + if (sci != IPV6_CONTEXT_IDENTIFIER_NONE) + { + // Set stateful source address compression. + iphc_buff[1] |= (IPHC_SAC_1 << IPHC_SAC_POS); + } + + if (IPV6_ADDRESS_IS_FULLY_ELIDABLE(p_interface->local_addr.identifier, + &p_iphdr->srcaddr) + || + sci_cover == true) + { + iphc_buff[1] |= (IPHC_SAM_11 << IPHC_SAM_POS); + } + else if (IPV6_ADDRESS_IS_16_BIT_COMPRESSABLE(&p_iphdr->srcaddr)) + { + iphc_buff[1] |= (IPHC_SAM_10 << IPHC_SAM_POS); + memcpy(p_iphc, &p_iphdr->srcaddr.u8[14], 2); + p_iphc += 2; + } + else + { + iphc_buff[1] |= (IPHC_SAM_01 << IPHC_SAM_POS); + memcpy(p_iphc, &p_iphdr->srcaddr.u8[8], 8); + p_iphc += 8; + } + } + else + { + // Carry full source address in-line. + iphc_buff[1] |= (IPHC_SAC_0 << IPHC_SAC_POS); + iphc_buff[1] |= (IPHC_SAM_00 << IPHC_SAM_POS); + memcpy(p_iphc, p_iphdr->srcaddr.u8, IPV6_ADDR_SIZE); + p_iphc += IPV6_ADDR_SIZE; + } + + // Destination compression. + if (IPV6_ADDRESS_IS_MULTICAST(&p_iphdr->destaddr)) + { + iphc_buff[1] |= (IPHC_M_1 << IPHC_M_POS); + + if (dci != IPV6_CONTEXT_IDENTIFIER_NONE) + { + iphc_buff[1] |= (IPHC_DAC_1 << IPHC_DAC_POS); + iphc_buff[1] |= (IPHC_DAM_00 << IPHC_DAM_POS); + + p_iphdr->destaddr.u8[0] = 0xff; + memcpy(p_iphc, &p_iphdr->destaddr.u8[1], 2); + memcpy(p_iphc + 2, &p_iphdr->destaddr.u8[12], 4); + p_iphc += 6; + } + else if (IPV6_ADDRESS_IS_8_BIT_MCAST_COMPRESSABLE(&p_iphdr->destaddr)) + { + iphc_buff[1] |= (IPHC_DAC_0 << IPHC_DAC_POS); + iphc_buff[1] |= (IPHC_DAM_11 << IPHC_DAM_POS); + *p_iphc++ = p_iphdr->destaddr.u8[15]; + + } + else if (IPV6_ADDRESS_IS_32_BIT_MCAST_COMPRESSABLE(&p_iphdr->destaddr)) + { + iphc_buff[1] |= (IPHC_DAC_0 << IPHC_DAC_POS); + iphc_buff[1] |= (IPHC_DAM_10 << IPHC_DAM_POS); + + *p_iphc = p_iphdr->destaddr.u8[1]; + memcpy(p_iphc + 1, &p_iphdr->destaddr.u8[13], 3); + p_iphc += 4; + } + else if (IPV6_ADDRESS_IS_48_BIT_MCAST_COMPRESSABLE(&p_iphdr->destaddr)) + { + iphc_buff[1] |= (IPHC_DAC_0 << IPHC_DAC_POS); + iphc_buff[1] |= (IPHC_DAM_01 << IPHC_DAM_POS); + + *p_iphc = p_iphdr->destaddr.u8[1]; + memcpy(p_iphc + 1, &p_iphdr->destaddr.u8[11], 5); + p_iphc += 6; + } + else + { + // Carry full destination multi-cast address in-line. + iphc_buff[1] |= (IPHC_DAC_0 << IPHC_DAC_POS); + iphc_buff[1] |= (IPHC_DAM_00 << IPHC_DAM_POS); + memcpy(p_iphc, p_iphdr->destaddr.u8, IPV6_ADDR_SIZE); + p_iphc += IPV6_ADDR_SIZE; + } + } + else + { + iphc_buff[1] |= (IPHC_M_0 << IPHC_M_POS); + + if (dci != IPV6_CONTEXT_IDENTIFIER_NONE || IPV6_ADDRESS_IS_LINK_LOCAL(&p_iphdr->destaddr)) + { + if (dci != IPV6_CONTEXT_IDENTIFIER_NONE) + { + iphc_buff[1] |= (IPHC_DAC_1 << IPHC_DAC_POS); + } + + if (IPV6_ADDRESS_IS_FULLY_ELIDABLE(p_interface->peer_addr.identifier, + &p_iphdr->destaddr) + || + dci_cover == true) + { + iphc_buff[1] |= (IPHC_DAM_11 << IPHC_DAM_POS); + } + else if (IPV6_ADDRESS_IS_16_BIT_COMPRESSABLE(&p_iphdr->destaddr)) + { + iphc_buff[1] |= (IPHC_DAM_10 << IPHC_DAM_POS); + memcpy(p_iphc, &p_iphdr->destaddr.u8[14], 2); + p_iphc += 2; + } + else + { + iphc_buff[1] |= (IPHC_DAM_01 << IPHC_DAM_POS); + memcpy(p_iphc, &p_iphdr->destaddr.u8[8], 8); + p_iphc += 8; + } + } + else + { + // Carry full destination address in-line. + iphc_buff[1] |= (IPHC_DAC_0 << IPHC_DAC_POS); + iphc_buff[1] |= (IPHC_DAM_00 << IPHC_DAM_POS); + memcpy(p_iphc, p_iphdr->destaddr.u8, IPV6_ADDR_SIZE); + p_iphc += IPV6_ADDR_SIZE; + } + } + + if ( iphc_buff[0] & IPHC_NH_MASK) + { + p_iphc += iphc_nhc_encode(p_iphc, p_input, &nhc_length); + } + + // Calculate IPHC size. + iphc_len = (p_iphc - iphc_buff); + + // Calculate IPv6 packet size. + *p_output_len = input_len - (IPV6_IP_HEADER_SIZE - iphc_len + nhc_length); + + // Use p_data as final buffer (since IPHC+NHC <= IPv6 Header + NHC (UDP)). + memcpy((uint8_t *)&p_input[IPV6_IP_HEADER_SIZE + nhc_length - iphc_len], iphc_buff, iphc_len); + + // Set correct address of output data. + *p_output = (uint8_t *) &p_input[IPV6_IP_HEADER_SIZE + nhc_length - iphc_len]; + + return NRF_SUCCESS; +} + + +/****************************************************************************** + * @name 6LoWPAN transport functions + *****************************************************************************/ + +/**@brief Function for notifying application of an error in an ongoing procedure. + * + * @param[in] p_interface Pointer to 6LoWPAN interface. + * @param[in] err_code Internally error code. + * + * @return None. + */ +static void app_notify_error(iot_interface_t * p_interface, + uint32_t err_code) +{ + ble_6lowpan_event_t event; + + event.event_id = BLE_6LO_EVT_ERROR; + event.event_result = err_code; + + BLE_6LOWPAN_MUTEX_UNLOCK(); + + m_event_handler(p_interface, &event); + + BLE_6LOWPAN_MUTEX_LOCK(); +} + + +/**@brief Function for notifying application of the new packet received. + * + * @param[in] p_interface Pointer to iot interface. + * @param[in] p_packet Pointer to decompressed IPv6 packet. + * @param[in] packet_len Length of IPv6 packet. + * @param[in] result Processing result of packet. Could be NRF_SUCCESS, or + * NRF_ERROR_NOT_FOUND if context identifier is unknown. + * @param[in] p_rx_contexts Received contexts if any. + * + * @return None. + */ +static void app_notify_rx_data(iot_interface_t * p_interface, + uint8_t * p_packet, + uint16_t packet_len, + uint32_t result, + iot_context_id_t * p_rx_contexts) +{ + ble_6lowpan_event_t event; + + event.event_id = BLE_6LO_EVT_INTERFACE_DATA_RX; + event.event_result = result; + event.event_param.rx_event_param.p_packet = p_packet; + event.event_param.rx_event_param.packet_len = packet_len; + event.event_param.rx_event_param.rx_contexts = *(p_rx_contexts); + + BLE_6LOWPAN_MUTEX_UNLOCK(); + + m_event_handler(p_interface, &event); + + BLE_6LOWPAN_MUTEX_LOCK(); +} + + +/**@brief Function for notifying application of the new interface established. + * + * @param[in] p_interface Pointer to new iot interface. + * + * @return None. + */ +static void app_notify_interface_add(iot_interface_t * p_interface) +{ + ble_6lowpan_event_t event; + + event.event_id = BLE_6LO_EVT_INTERFACE_ADD; + event.event_result = NRF_SUCCESS; + + BLE_6LOWPAN_MUTEX_UNLOCK(); + + m_event_handler(p_interface, &event); + + BLE_6LOWPAN_MUTEX_LOCK(); +} + + +/**@brief Function for notifying application of the interface disconnection. + * + * @param[in] p_interface Pointer to removed iot interface. + * + * @return None. + */ +static void app_notify_interface_delete(iot_interface_t * p_interface) +{ + ble_6lowpan_event_t event; + + event.event_id = BLE_6LO_EVT_INTERFACE_DELETE; + event.event_result = NRF_SUCCESS; + + BLE_6LOWPAN_MUTEX_UNLOCK(); + + m_event_handler(p_interface, &event); + + BLE_6LOWPAN_MUTEX_LOCK(); +} + + +/**@brief Function for initialize transmit FIFO. + * + * @param[in] p_fifo Pointer to transmit FIFO instance. + * + * @return None. + */ +static void tx_fifo_init(tx_fifo_t * p_fifo) +{ + memset(p_fifo->packets, 0, BLE_6LOWPAN_TX_FIFO_SIZE * sizeof (tx_packet_t)); + p_fifo->read_pos = 0; + p_fifo->write_pos = 0; +} + + +/**@brief Function for putting new packet to transmit FIFO. + * + * @param[in] p_fifo Pointer to transmit FIFO instance. + * @param[in] p_packet Pointer to new packet. + * + * @return NRF_SUCCESS on success, otherwise NRF_ERROR_NO_MEM error. + */ +static uint32_t tx_fifo_put(tx_fifo_t * p_fifo, tx_packet_t * p_packet) +{ + uint32_t err_code = BLE_6LOWPAN_TX_FIFO_FULL; + + // To prevent "The order of volatile accesses is undefined in this statement" + // in subsequent conditional statement. + uint32_t write_pos = p_fifo->write_pos; + uint32_t read_pos = p_fifo->read_pos; + + if ((write_pos - read_pos) <= TX_FIFO_MASK) + { + p_fifo->packets[p_fifo->write_pos & TX_FIFO_MASK].p_data = p_packet->p_data; + p_fifo->packets[p_fifo->write_pos & TX_FIFO_MASK].data_len = p_packet->data_len; + p_fifo->packets[p_fifo->write_pos & TX_FIFO_MASK].p_mem_block = p_packet->p_mem_block; + + p_fifo->write_pos++; + + err_code = NRF_SUCCESS; + } + + return err_code; +} + + +/**@brief Function for popping currently processed packet in transmit FIFO. + * It releases element on FIFO only when processing of the element is done. + * + * @param[in] p_fifo Pointer to transmit FIFO instance. + * + * @return None. + */ +static void tx_fifo_release(tx_fifo_t * p_fifo) +{ + p_fifo->read_pos++; +} + + +/**@brief Function for reading front element of transmit FIFO. + * After finish processing element in queue, it must be + * released using tx_fifo_release function. + * + * @param[in] p_fifo Pointer to transmit FIFO instance. + * @param[in] pp_packet Pointer to front packet. + * + * @return NRF_SUCCESS on success, otherwise NRF_ERROR_NO_MEM error. + */ +static uint32_t tx_fifo_get(tx_fifo_t * p_fifo, tx_packet_t * * pp_packet) +{ + uint32_t err_code = NRF_ERROR_NOT_FOUND; + + // To prevent "The order of volatile accesses is undefined in this statement" + // in subsequent conditional statement. + uint32_t write_pos = p_fifo->write_pos; + uint32_t read_pos = p_fifo->read_pos; + + if ((write_pos - read_pos) != 0) + { + *pp_packet = &p_fifo->packets[p_fifo->read_pos & TX_FIFO_MASK]; + err_code = NRF_SUCCESS; + } + + return err_code; +} + + +/**@brief Function for searching transport interface by given IPSP handle. + * + * @param[in] p_ipsp_handle Pointer to IPSP handle. + * + * @return Transport interface related with IPSP handle, or NULL if not found. + */ +static transport_instance_t * interface_get_by_handle(const ble_ipsp_handle_t * p_ipsp_handle) +{ + uint32_t index; + + for (index = 0; index < BLE_6LOWPAN_MAX_INTERFACE; index++) + { + if (m_instances[index].handle.cid == p_ipsp_handle->cid && + m_instances[index].handle.conn_handle == p_ipsp_handle->conn_handle) + { + return &m_instances[index]; + } + } + + return NULL; +} + + +/**@brief Function for initializing transport instance. + * + * @param[in] index Index of instance. + * + * @return None. + */ +static void instance_init(uint32_t index) +{ + memset(&m_instances[index], 0, sizeof (transport_instance_t)); + m_instances[index].handle.cid = BLE_L2CAP_CID_INVALID; + m_instances[index].p_tx_cur_packet = NULL; + m_instances[index].interface.p_transport = (void *) index; +} + + +/**@brief Function for resetting specific 6lowpan interface. + * + * @param[in] p_interface Pointer to transport interface. + * + * @return None. + */ +static void interface_reset(transport_instance_t * p_instance) +{ + uint32_t index; + uint32_t instance_id = (uint32_t) p_instance->interface.p_transport; + + // Free all queued packets. + for (index = 0; index < BLE_6LOWPAN_TX_FIFO_SIZE; index++) + { + if (m_instances[instance_id].tx_fifo.packets[index].p_mem_block != NULL) + { + nrf_free(m_instances[instance_id].tx_fifo.packets[index].p_mem_block); + } + } + + instance_init (instance_id); +} + + +/**@brief Function for adding new transport instance. + * + * @param[in] p_peer_addr Pointer EUI-64 of peer address. + * @param[in] p_ipsp_handle Pointer IPSP handle, related with L2CAP CoC channel. + * @param[out] pp_instance Pointer to added transport instances, if operation succeeded. + * + * @return NRF_SUCCESS on success, otherwise NRF_ERROR_NO_MEM error. + */ +static uint32_t interface_add(const eui64_t * p_peer_addr, + const ble_ipsp_handle_t * p_ipsp_handle, + transport_instance_t ** pp_instance) +{ + uint32_t index; + + for (index = 0; index < BLE_6LOWPAN_MAX_INTERFACE; index++) + { + if (m_instances[index].handle.cid == BLE_L2CAP_CID_INVALID) + { + m_instances[index].handle.cid = p_ipsp_handle->cid; + m_instances[index].handle.conn_handle = p_ipsp_handle->conn_handle; + m_instances[index].interface.tx_contexts.src_cntxt_id = IPV6_CONTEXT_IDENTIFIER_NONE; + m_instances[index].interface.tx_contexts.dest_cntxt_id = IPV6_CONTEXT_IDENTIFIER_NONE; + + memcpy(&m_instances[index].interface.peer_addr, p_peer_addr, sizeof (eui64_t)); + memcpy(&m_instances[index].interface.local_addr, &m_ll_addr, sizeof (eui64_t)); + + // Initialize TX FIFO. + tx_fifo_init(&m_instances[index].tx_fifo); + + *pp_instance = &m_instances[index]; + + return NRF_SUCCESS; + } + } + + return NRF_ERROR_NO_MEM; +} + + +/**@brief Function for checking if any transmission is currently processing on specific interface. + * Current version of L2CAP CoC in SoftDevice has limitation to send one packet at same + * time. + * + * @param[in] p_instance Pointer to transport instance. + * + * @return TRUE if interface not sending any packets, FALSE if busy. + */ +static bool tx_is_free(transport_instance_t * p_instance) +{ + return (NULL == p_instance->p_tx_cur_packet); +} + + +/**@brief Function uses for indicating transmission complete on specific interface. + * + * @param[in] p_instance Pointer to transport instance. + * + * @return None. + */ +static void tx_complete(transport_instance_t * p_instance) +{ + BLE_6LOWPAN_TRC("[CID 0x%04X]: Transmission complete.", + p_instance->handle.cid); + + // Free the transmit buffer. + nrf_free(p_instance->p_tx_cur_packet->p_mem_block); + p_instance->p_tx_cur_packet = NULL; + + // Release last processed packet. + tx_fifo_release(&p_instance->tx_fifo); +} + + +/**@brief Function for sending front packet from transmit FIFO on specific interface. + * + * @param[in] p_instance Pointer to transport instance. + * + * @return None. + */ +static void tx_send(transport_instance_t * p_instance) +{ + uint32_t err_code = NRF_SUCCESS; + + if (NRF_SUCCESS == tx_fifo_get(&p_instance->tx_fifo, + &p_instance->p_tx_cur_packet)) + { + err_code = ble_ipsp_send(&p_instance->handle, + p_instance->p_tx_cur_packet->p_data, + p_instance->p_tx_cur_packet->data_len); + + if (NRF_SUCCESS != err_code) + { + BLE_6LOWPAN_TRC("Cannot send the packet, error = 0x%08lX", err_code); + + app_notify_error(&p_instance->interface, err_code); + + tx_complete(p_instance); + + // Try to send another packet. + tx_send(p_instance); + } + } +} + +/**@brief Callback registered with IPSP to receive asynchronous events from the module. + * + * @param[in] p_handle Pointer to IPSP handle. + * @param[in] p_evt Pointer to specific event, generated by IPSP module. + * + * @return NRF_SUCCESS on success, otherwise NRF_ERROR_NO_MEM error. + */ +static uint32_t ipsp_evt_handler(ble_ipsp_handle_t const * p_handle, ble_ipsp_evt_t const * p_evt) +{ + BLE_6LOWPAN_ENTRY(); + + VERIFY_MODULE_IS_INITIALIZED(); + + uint32_t mem_size; + uint16_t buff_len; + eui64_t peer_addr; + iot_context_id_t rx_contexts; + uint32_t retval = NRF_SUCCESS; + transport_instance_t * p_instance = NULL; + uint8_t * p_buff = NULL; + + BLE_6LOWPAN_MUTEX_LOCK(); + + p_instance = interface_get_by_handle(p_handle); + + switch (p_evt->evt_id) + { + case BLE_IPSP_EVT_CHANNEL_CONNECTED: + { + BLE_6LOWPAN_TRC("[CID 0x%04X]: >> BLE_IPSP_EVT_CHANNEL_CONNECTED", + p_handle->cid); + BLE_6LOWPAN_TRC("New channel established."); + + if (p_instance == NULL) + { + IPV6_EUI64_CREATE_FROM_EUI48(peer_addr.identifier, + p_evt->p_evt_param->p_peer->addr, + p_evt->p_evt_param->p_peer->addr_type); + + // Add interface to internal table. + retval = interface_add(&peer_addr, p_handle, &p_instance); + + if (NRF_SUCCESS == retval) + { + BLE_6LOWPAN_TRC("Added new IPSP interface."); + + // Notify application. + app_notify_interface_add(&p_instance->interface); + } + else + { + BLE_6LOWPAN_ERR("Cannot add new interface. Table is full."); + UNUSED_VARIABLE(ble_ipsp_disconnect(p_handle)); + } + } + else + { + // Got a connection event, when already connected. + UNUSED_VARIABLE(ble_ipsp_disconnect(p_handle)); + } + + break; + } + + case BLE_IPSP_EVT_CHANNEL_DISCONNECTED: + { + BLE_6LOWPAN_TRC("[CID 0x%04X]: >> BLE_IPSP_EVT_CHANNEL_DISCONNECTED", + p_handle->cid); + BLE_6LOWPAN_TRC("Channel disconnection."); + + if (NULL != p_instance) + { + BLE_6LOWPAN_TRC("Removed IPSP interface."); + + // Notify application. + app_notify_interface_delete(&p_instance->interface); + + // Remove interface from internal table. + interface_reset(p_instance); + } + + break; + } + + case BLE_IPSP_EVT_CHANNEL_DATA_RX: + { + + if (NULL != p_instance) + { + const uint16_t packet_len = MIN(p_evt->p_evt_param->p_l2cap_evt->params.rx.sdu_buf.len, + p_evt->p_evt_param->p_l2cap_evt->params.rx.sdu_len); + + BLE_6LOWPAN_TRC("[CID 0x%04X]: >> BLE_IPSP_EVT_CHANNEL_DATA_RX", + p_handle->cid); + + BLE_6LOWPAN_DUMP(p_evt->p_evt_param->p_l2cap_evt->params.rx.sdu_buf.p_data, + packet_len); + + BLE_6LOWPAN_TRC("Processing received data."); + + mem_size = packet_len + IPHC_MAX_COMPRESSED_DIFF; + + // Try to allocate memory for incoming data. + retval = nrf_mem_reserve(&p_buff, &mem_size); + + if (retval == NRF_SUCCESS) + { + // Decompress IPHC data into IPv6 packet. + retval = iphc_decode(&p_instance->interface, + p_buff, + &buff_len, + p_evt->p_evt_param->p_l2cap_evt->params.rx.sdu_buf.p_data, + packet_len, + &rx_contexts); + + // Do not discard if packet decompressed successfully, + // otherwise error in Context based decompression. + if (retval == NRF_SUCCESS || retval == BLE_6LOWPAN_CID_NOT_FOUND) + { + if ((p_evt->evt_result == NRF_ERROR_BLE_IPSP_RX_PKT_TRUNCATED) && + (retval == NRF_SUCCESS)) + { + // Inform the application that the packet is truncated. + retval = NRF_ERROR_BLE_IPSP_RX_PKT_TRUNCATED; + } + + BLE_6LOWPAN_TRC("Decompressed packet:"); + BLE_6LOWPAN_DUMP(p_buff, buff_len); + + // Notify application. + app_notify_rx_data(&p_instance->interface, + p_buff, + buff_len, + retval, + &rx_contexts); + } + else + { + BLE_6LOWPAN_ERR("Decompression failed!"); + + nrf_free(p_buff); + } + } + else + { + BLE_6LOWPAN_ERR( + "No buffer in memory pool available, packet dropped!"); + } + } + else + { + BLE_6LOWPAN_ERR("Got data to unknown interface!"); + } + + break; + } + + case BLE_IPSP_EVT_CHANNEL_DATA_TX_COMPLETE: + { + BLE_6LOWPAN_TRC("[CID 0x%04X]: >> BLE_IPSP_EVT_CHANNEL_DATA_TX_COMPLETE", + p_handle->cid); + + // Free TX buffer. + tx_complete(p_instance); + + // Try to send another packet. + tx_send(p_instance); + + break; + } + + default: + break; + } + + BLE_6LOWPAN_MUTEX_UNLOCK(); + + BLE_6LOWPAN_EXIT(); + + return retval; +} + +/****************************************************************************** + * @name 6LoWPAN API functions + *****************************************************************************/ + +uint32_t ble_6lowpan_init(const ble_6lowpan_init_t * p_init) +{ + BLE_6LOWPAN_ENTRY(); + + uint32_t index; + uint32_t retval = NRF_SUCCESS; + ble_ipsp_init_t ipsp_init_params; + + NULL_PARAM_CHECK(p_init); + NULL_PARAM_CHECK(p_init->p_eui64); + NULL_PARAM_CHECK(p_init->event_handler); + + // Check if the transmit FIFO size is a power of two. + if (!IS_POWER_OF_TWO(BLE_6LOWPAN_TX_FIFO_SIZE)) + { + return NRF_ERROR_INVALID_LENGTH | BLE_6LOWPAN_ERR_BASE; + } + + SDK_MUTEX_INIT(m_6lowpan_mutex); + + BLE_6LOWPAN_MUTEX_LOCK(); + + // Store EUI64 in internal variable. + memcpy(m_ll_addr.identifier, p_init->p_eui64->identifier, EUI_64_ADDR_SIZE); + + // Set application event handler. + m_event_handler = p_init->event_handler; + + // Clear transport interfaces table. + for (index = 0; index < BLE_6LOWPAN_MAX_INTERFACE; index++) + { + instance_init(index); + } + + // IPSP module initialization. + ipsp_init_params.evt_handler = ipsp_evt_handler; + + // Initialize the IPSP module. + retval = ble_ipsp_init(&ipsp_init_params); + + BLE_6LOWPAN_MUTEX_UNLOCK(); + + BLE_6LOWPAN_EXIT(); + + return retval; +} + + +uint32_t ble_6lowpan_interface_disconnect(const iot_interface_t * p_interface) +{ + BLE_6LOWPAN_ENTRY(); + + VERIFY_MODULE_IS_INITIALIZED(); + + NULL_PARAM_CHECK(p_interface); + + uint32_t retval; + transport_instance_t * p_instance = &m_instances[(uint32_t)p_interface->p_transport]; + + BLE_6LOWPAN_MUTEX_LOCK(); + + retval = ble_ipsp_disconnect(&p_instance->handle); + + BLE_6LOWPAN_MUTEX_UNLOCK(); + + BLE_6LOWPAN_EXIT(); + + return retval; +} + + +uint32_t ble_6lowpan_interface_send(const iot_interface_t * p_interface, + const uint8_t * p_packet, + uint16_t packet_len) +{ + BLE_6LOWPAN_ENTRY(); + + VERIFY_MODULE_IS_INITIALIZED(); + + NULL_PARAM_CHECK(p_packet); + NULL_PARAM_CHECK(p_interface); + PACKET_LENGTH_CHECK(packet_len); + + uint32_t retval = NRF_SUCCESS; + uint8_t * p_output_buff = NULL; + uint16_t output_len; + tx_packet_t tx_packet; + transport_instance_t * p_instance = &m_instances[(uint32_t)p_interface->p_transport]; + + BLE_6LOWPAN_MUTEX_LOCK(); + + BLE_6LOWPAN_TRC("Uncompressed packet:"); + BLE_6LOWPAN_DUMP((uint8_t *)p_packet, packet_len); + + // Encode IP packet into IPHC. + retval = iphc_encode(p_interface, + &p_output_buff, + &output_len, + p_packet, + packet_len); + + if (NRF_SUCCESS == retval) + { + BLE_6LOWPAN_TRC("Successfully compressed packet."); + + tx_packet.p_data = p_output_buff; + tx_packet.data_len = output_len; + tx_packet.p_mem_block = (uint8_t *)p_packet; + + retval = tx_fifo_put(&p_instance->tx_fifo, &tx_packet); + + if (NRF_SUCCESS == retval) + { + BLE_6LOWPAN_TRC("Compressed packet:"); + BLE_6LOWPAN_DUMP(p_output_buff, output_len); + + // Send packet immediately if transport interface is not busy. + if (tx_is_free(p_instance)) + { + tx_send(p_instance); + } + } + else + { + BLE_6LOWPAN_ERR("No place in TX queue!"); + } + } + else + { + BLE_6LOWPAN_ERR("Error while compression!"); + } + + BLE_6LOWPAN_MUTEX_UNLOCK(); + + BLE_6LOWPAN_EXIT(); + + return retval; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.h new file mode 100644 index 0000000..267ff30 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ble_6lowpan/ble_6lowpan.h @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file ble_6lowpan.h + * + * @defgroup ble_6lowpan BLE 6LoWPAN library + * @ingroup iot_sdk_6lowpan + * @{ + * @brief 6LoWPAN techniques defined for BLE. + * + * @details This module implements 6LoWPAN techniques defined for BLE, including + * IP and UDP header compression and decompression and conversion of EUI-48 BLE + * addresses to EUI-64 and on to IPv6 addresses. This layer does not implement IP level + * functionality of neighbor discovery etc. + * @note Currently, only the 6LoWPAN node (host) role is supported. + */ + +#ifndef BLE_6LOWPAN_H__ +#define BLE_6LOWPAN_H__ + +#include <stdint.h> +#include "iot_defines.h" +#include "iot_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Maximum 6LoWPAN interface supported by module. */ +#define BLE_6LOWPAN_MAX_INTERFACE 1 + +/**@brief Maximum transmit packets that are buffered per interface. + * + * @details FIFO size must be a power of 2. + */ +#define BLE_6LOWPAN_TX_FIFO_SIZE 16 + +/**@brief Asynchronous event identifiers type. */ +typedef enum +{ + BLE_6LO_EVT_ERROR, /**< Notification of an error in the module. */ + BLE_6LO_EVT_INTERFACE_ADD, /**< Notification of a new 6LoWPAN interface added. */ + BLE_6LO_EVT_INTERFACE_DELETE, /**< Notification of a 6LoWPAN interface deleted. */ + BLE_6LO_EVT_INTERFACE_DATA_RX, /**< Notification of an IP packet received on the interface. */ +} ble_6lowpan_event_id_t; + +/**@brief Event parameters associated with the BLE_6LO_EVT_INTERFACE_DATA_RX event. */ +typedef struct +{ + uint8_t * p_packet; /**< Uncompressed IPv6 packet received. This memory is available to the application until it is freed by the application using mem_free. */ + uint16_t packet_len; /**< Length of IPv6 packet. */ + iot_context_id_t rx_contexts; /**< RX contexts used in stateful decompression. IPV6_CONTEXT_IDENTIFIER_NONE if not used. */ +} ble_6lowpan_data_rx_t; + +/**@brief Asynchronous event parameter type. */ +typedef union +{ + ble_6lowpan_data_rx_t rx_event_param; /**< Parameters notified with the received packet. */ +} ble_6lowpan_event_param_t; + +/**@brief Asynchronous event type. */ +typedef struct +{ + ble_6lowpan_event_id_t event_id; /**< Event identifier. */ + ble_6lowpan_event_param_t event_param; /**< Event parameters. */ + uint32_t event_result; /**< Result of event being notified. */ +} ble_6lowpan_event_t; + +/**@brief Asynchronous event notification callback type. */ +typedef void (*ble_6lowpan_evt_handler_t)(iot_interface_t * p_interface, + ble_6lowpan_event_t * p_event); + + +/**@brief Initialization parameters type. */ +typedef struct +{ + eui64_t * p_eui64; /**< EUI-64 address. */ + ble_6lowpan_evt_handler_t event_handler; /**< Asynchronous event notification callback registered to receive 6LoWPAN events. */ +} ble_6lowpan_init_t; + + +/**@brief Initializes the module. + * + * @param[in] p_init Initialization parameters. + * + * @retval NRF_SUCCESS If initialization was successful. Otherwise, an error code is returned. + */ +uint32_t ble_6lowpan_init(const ble_6lowpan_init_t * p_init); + + +/**@brief Disconnects 6LoWPAN interface. + * + * @details This function is used to terminate connection on the L2CAP CoC layer. It calls + * \ref ble_ipsp_disconnect to perform disconnection procedure. The registered callback + * from 6LoWPAN module propagates \ref BLE_6LO_EVT_INTERFACE_DELETE event after + * operation is finished. + * + * @param[in] p_interface Identifies the interface on which the disconnection has to be performed. + * + * @retval NRF_SUCCESS If disconnection was successful. Otherwise, an error code is returned. + */ +uint32_t ble_6lowpan_interface_disconnect(const iot_interface_t * p_interface); + + +/**@brief Sends IPv6 packet on the 6LoWPAN interface. + * + * @details This function is used to send an IPv6 packet on the interface. 6LoWPAN compression + * techniques are applied on the packet before the packet is transmitted. The packet might + * not be transferred to the peer immediately based on the flow control on the BLE Link. + * In this case, the packet is queued to be transferred later. + * + * @param[in] p_interface Identifies the interface on which the packet is to be sent. + * @param[in] p_packet IPv6 packet to be sent. Memory for the packet should be allocated + * using mem_alloc and should not be freed. The module is responsible for + * freeing the memory using mem_free. The module will free the packet once + * the transmission is complete or the packet can no longer be transmitted + * (in case of link disconnection.) + * @param[in] packet_len Length of the IPv6 packet. + * + * @retval NRF_SUCCESS If the send request was successful. + */ +uint32_t ble_6lowpan_interface_send(const iot_interface_t * p_interface, + const uint8_t * p_packet, + uint16_t packet_len); + +#ifdef __cplusplus +} +#endif + +#endif //BLE_6LOWPAN_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap.c new file mode 100644 index 0000000..4d58384 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap.c @@ -0,0 +1,854 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdbool.h> +#include <string.h> +#include "nordic_common.h" +#include "nrf.h" +#include "coap_api.h" +#include "coap.h" +#include "coap_queue.h" +#include "coap_transport.h" +#include "sdk_common.h" +#include "iot_common.h" +#include "mem_manager.h" +#include "coap_resource.h" +#include "coap_observe_api.h" +#include "coap_observe.h" + +#if IOT_COAP_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME coap + +#define NRF_LOG_LEVEL IOT_COAP_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR IOT_COAP_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR IOT_COAP_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define COAP_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define COAP_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define COAP_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define COAP_ENTRY() COAP_TRC(">> %s", __func__) +#define COAP_EXIT() COAP_TRC("<< %s", __func__) +#define COAP_EXIT_WITH_RESULT(result) COAP_TRC("<< %s, result: %d", __func__, result) + +#else // IOT_COAP_CONFIG_LOG_ENABLED + +#define COAP_TRC(...) /**< Disables traces. */ +#define COAP_DUMP(...) /**< Disables dumping of octet streams. */ +#define COAP_ERR(...) /**< Disables error logs. */ + +#define COAP_ENTRY(...) +#define COAP_EXIT(...) +#define COAP_EXIT_WITH_RESULT(...) + +#endif // IOT_COAP_CONFIG_LOG_ENABLED + +#define COAP_REQUEST_ENTITY_MAX_SIZE (BLE_IPSP_RX_BUFFER_SIZE - (IPV6_IP_HEADER_SIZE + \ + UDP_HEADER_SIZE)) /** Maximum request entity size. */ + +SDK_MUTEX_DEFINE(m_coap_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ + +static uint32_t m_token_seed; /**< Token seed provided by application to be used for generating token numbers. */ +static uint32_t m_message_id_counter; /**< Message ID counter, used to generate unique message IDs. */ +static coap_error_callback_t m_error_callback; /**< Function pointer to an application CoAP error handler. */ + +static coap_request_handler_t m_request_handler = NULL; /**< Request handler where to forward all incoming requests. */ + +#define COAP_MESSAGE_ACK_SET(REMOTE, LOCAL_PORT, MID) { \ + memcpy(&m_coap_empty_message.remote, (REMOTE), sizeof(coap_remote_t)); \ + m_coap_empty_message.port.port_number = (LOCAL_PORT); \ + m_coap_empty_message.header.id = (MID); \ + m_coap_empty_message.header.type = COAP_TYPE_ACK; \ +} + +#define COAP_MESSAGE_RST_SET(REMOTE, LOCAL_PORT, MID) { \ + memcpy(&m_coap_empty_message.remote, (REMOTE), sizeof(coap_remote_t)); \ + m_coap_empty_message.port.port_number = (LOCAL_PORT); \ + m_coap_empty_message.header.id = (MID); \ + m_coap_empty_message.header.type = COAP_TYPE_RST; \ +} + +static coap_message_t m_coap_empty_message = { + .header = { + .version = 1, + .type = COAP_TYPE_ACK, + .token_len = 0, + .code = COAP_CODE_EMPTY_MESSAGE, + .id = 0, + }, + .p_payload = NULL, + .payload_len = 0, + .options_count = 0, + .p_arg = NULL, + .response_callback = NULL, + .port = { + .port_number = 0 + }, + .options_len = 0, + .options_offset = 0, + .p_data = NULL, + .data_len = 0 +}; + +static inline bool is_ping(coap_message_t * p_message) +{ + return (p_message->header.code == COAP_CODE_EMPTY_MESSAGE) && + (p_message->header.type == COAP_TYPE_CON); +} + +static inline bool is_ack(coap_message_t * p_message) +{ + return (p_message->header.code == COAP_CODE_EMPTY_MESSAGE) && + (p_message->header.type == COAP_TYPE_ACK); +} + +static inline bool is_reset(coap_message_t * p_message) +{ + return (p_message->header.type == COAP_TYPE_RST); +} + +static inline bool is_con(coap_message_t * p_message) +{ + return (p_message->header.type == COAP_TYPE_CON); +} + +static inline bool is_non(coap_message_t * p_message) +{ + return (p_message->header.type == COAP_TYPE_NON); +} + +static inline bool is_request(uint8_t message_code) +{ + return (message_code >= 1) && (message_code < 32); +} + +static inline bool is_response(uint8_t message_code) +{ + return (message_code >= 64) && (message_code < 192); +} + +static inline void app_error_notify(uint32_t err_code, coap_message_t * p_message) +{ + if (m_error_callback != NULL) + { + COAP_MUTEX_UNLOCK(); + + m_error_callback(err_code, p_message); + + COAP_MUTEX_LOCK(); + } +} + +uint32_t coap_init(uint32_t token_rand_seed, coap_transport_init_t * p_transport_param) +{ + COAP_ENTRY(); + + uint32_t err_code; + + SDK_MUTEX_INIT(m_coap_mutex); + + COAP_MUTEX_LOCK(); + + internal_coap_observe_init(); + + m_error_callback = NULL; + + m_token_seed = token_rand_seed; + (void)m_token_seed; + + m_message_id_counter = 1; + + err_code = coap_transport_init(p_transport_param); + if (err_code != NRF_SUCCESS) + { + COAP_MUTEX_UNLOCK(); + return err_code; + } + + err_code = coap_queue_init(); + if (err_code != NRF_SUCCESS) + { + COAP_MUTEX_UNLOCK(); + return err_code; + } + + err_code = coap_resource_init(); + + COAP_MUTEX_UNLOCK(); + + COAP_EXIT(); + + return err_code; + +} + +uint32_t coap_error_handler_register(coap_error_callback_t error_callback) +{ + // TODO: error handling, null pointer, module initilized etc. + COAP_MUTEX_LOCK(); + + m_error_callback = error_callback; + + COAP_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + +uint32_t internal_coap_message_send(uint32_t * p_handle, coap_message_t * p_message) +{ + if (p_message == NULL) + { + return (NRF_ERROR_NULL | IOT_COAP_ERR_BASE); + } + + // Compiled away if COAP_ENABLE_OBSERVE_CLIENT is not set to 1. + coap_observe_client_send_handle(p_message); + + COAP_ENTRY(); + + // Fetch the expected length of the packet serialized by passing length of 0. + uint16_t expected_length = 0; + uint32_t err_code = coap_message_encode(p_message, NULL, &expected_length); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Allocate a buffer to serialize the message into. + uint8_t * p_buffer; + uint32_t request_length = expected_length; + err_code = nrf_mem_reserve(&p_buffer, &request_length); + if (err_code != NRF_SUCCESS) + { + COAP_TRC("p_buffer alloc error = 0x%08lX!", err_code); + return err_code; + } + memset(p_buffer, 0, request_length); + COAP_TRC("Alloc mem, p_buffer = %p", (uint8_t *)p_buffer); + + // Serialize the message. + uint16_t buffer_length = (uint16_t)request_length; + err_code = coap_message_encode(p_message, p_buffer, &buffer_length); + if (err_code != NRF_SUCCESS) + { + COAP_TRC("Encode error!"); + COAP_TRC("Free mem, p_buffer = %p", p_buffer); + UNUSED_VARIABLE(nrf_free(p_buffer)); + + return err_code; + } + + err_code = coap_transport_write(&p_message->port, &p_message->remote, p_buffer, buffer_length); + + if (err_code == NRF_SUCCESS) + { + if (is_con(p_message) || (is_non(p_message) && + is_request(p_message->header.code) && + (p_message->response_callback != NULL))) + { + coap_queue_item_t item; + item.p_arg = p_message->p_arg; + item.mid = p_message->header.id; + item.callback = p_message->response_callback; + item.p_buffer = p_buffer; + item.buffer_len = buffer_length; + item.timeout_val = COAP_ACK_TIMEOUT * COAP_ACK_RANDOM_FACTOR; + + if (p_message->header.type == COAP_TYPE_CON) + { + item.timeout = item.timeout_val; + item.retrans_count = 0; + } + else + { + item.timeout = COAP_MAX_TRANSMISSION_SPAN; + item.retrans_count = COAP_MAX_RETRANSMIT_COUNT; + } + + item.port = p_message->port; + item.token_len = p_message->header.token_len; + + memcpy(&item.remote, &p_message->remote, sizeof(coap_remote_t)); + memcpy(item.token, p_message->token, p_message->header.token_len); + + err_code = coap_queue_add(&item); + if (err_code != NRF_SUCCESS) + { + COAP_TRC("Message queue error = 0x%08lX!", err_code); + + COAP_TRC("Free mem, p_buffer = %p", p_buffer); + UNUSED_VARIABLE(nrf_free(p_buffer)); + + return err_code; + } + + *p_handle = item.handle; + } + else + { + *p_handle = COAP_MESSAGE_QUEUE_SIZE; + + COAP_TRC("Free mem, p_buffer = %p", p_buffer); + UNUSED_VARIABLE(nrf_free(p_buffer)); + } + } + else + { + COAP_TRC("Free mem, p_buffer = %p", p_buffer); + UNUSED_VARIABLE(nrf_free(p_buffer)); + } + + COAP_EXIT(); + + return err_code; +} + + +static uint32_t create_response(coap_message_t ** pp_response, coap_message_t * p_request, uint16_t data_size) +{ + uint32_t err_code; + + // Allocate space for a new message. + uint32_t size = sizeof(coap_message_t); + err_code = nrf_mem_reserve((uint8_t **)pp_response, &size); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + coap_message_t * p_response = (*pp_response); + + memset(p_response, 0, sizeof(coap_message_t)); + COAP_TRC("Alloc mem, p_response = %p", (uint8_t *)p_response); + + if (data_size > 0) + { + // Allocate a scratch buffer for payload and options. + size = data_size; + err_code = nrf_mem_reserve(&(p_response->p_data), &size); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + memset(p_response->p_data, 0, size); + p_response->data_len = size; + COAP_TRC("Alloc mem, p_response->p_data = %p", p_response->p_data); + } + + coap_message_conf_t config; + memset (&config, 0, sizeof(coap_message_conf_t)); + + config.token_len = p_request->header.token_len; + config.id = p_request->header.id; + config.code = COAP_CODE_404_NOT_FOUND; + config.port.port_number = p_request->port.port_number; + + memcpy(config.token, p_request->token, p_request->header.token_len); + + if ((coap_msg_type_t)p_request->header.type == COAP_TYPE_CON) + { + config.type = COAP_TYPE_ACK; + } + else + { + config.type = (coap_msg_type_t)p_request->header.type; + } + + err_code = coap_message_create(p_response, &config); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + (void)coap_message_remote_addr_set(p_response, &p_request->remote); + + return NRF_SUCCESS; +} + + +/**@brief Common function for sending response error message + * + * @param[in] p_message Pointer to the original request message. + * @param[in] code CoAP message code to send in the reponse. + * + * @retval NRF_SUCCESS If the response was sent out successfully. + */ +static uint32_t send_error_response(coap_message_t * p_message, uint8_t code) +{ + coap_message_t * p_error_response = NULL; + + uint32_t err_code = create_response(&p_error_response, p_message, COAP_MESSAGE_DATA_MAX_SIZE); + if (err_code != NRF_SUCCESS) + { + // If message could not be created, notify the application. + app_error_notify(err_code, p_message); + return err_code; + } + + // Set the response code. + p_error_response->header.code = code; + + if (p_error_response != NULL) + { + uint32_t handle; + err_code = internal_coap_message_send(&handle, p_error_response); + + COAP_TRC("Free mem, p_response->p_data = %p", p_error_response->p_data); + UNUSED_VARIABLE(nrf_free(p_error_response->p_data)); + + COAP_TRC("Free mem, p_response = %p", (uint8_t *)p_error_response); + UNUSED_VARIABLE(nrf_free((uint8_t *)p_error_response)); + } + + return err_code; +} + +uint32_t coap_transport_read(const coap_port_t * p_port, + const coap_remote_t * p_remote, + const coap_remote_t * p_local, + uint32_t result, + const uint8_t * p_data, + uint16_t datalen) +{ + COAP_ENTRY(); + + // Discard all packets if not success or truncated. + if (!(result == NRF_SUCCESS || result == UDP_TRUNCATED_PACKET)) + { + return NRF_SUCCESS; + } + + uint32_t err_code; + coap_message_t * p_message; + + // Allocate space for a new message. + uint32_t size = sizeof(coap_message_t); + err_code = nrf_mem_reserve((uint8_t **)&p_message, &size); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + memset(p_message, 0, sizeof(coap_message_t)); + COAP_TRC("Alloc mem, p_message = %p", (uint8_t *)p_message); + + err_code = coap_message_decode(p_message, p_data, datalen); + if (err_code != NRF_SUCCESS) + { + app_error_notify(err_code, p_message); + + UNUSED_VARIABLE(nrf_free((uint8_t *)p_message)); + return err_code; + } + + // Copy the remote address information. + memcpy(&p_message->remote, p_remote, sizeof(coap_remote_t)); + + // Copy the destination address information. + if (p_local) + { + memcpy(&p_message->local, p_local, sizeof(coap_remote_t)); + } + + // Copy the local port information. + memcpy(&p_message->port, p_port, sizeof(coap_port_t)); + + if (result == UDP_TRUNCATED_PACKET) + { + app_error_notify(result, p_message); + } + else if (is_ping(p_message)) + { + COAP_MESSAGE_RST_SET(&p_message->remote, p_message->port.port_number, p_message->header.id); + + uint32_t handle; + err_code = internal_coap_message_send(&handle, &m_coap_empty_message); + } + else if (is_ack(p_message) || + is_reset(p_message)) + { + // Populate the token with the one used sending, before passing it to the application. + coap_queue_item_t * p_item = NULL; + err_code = coap_queue_item_by_mid_get(&p_item, p_message->header.id); + + if (err_code == NRF_SUCCESS) + { + if (p_item->callback != NULL) + { + // As the token is missing from peer, it will be added before giving it to the application. + memcpy(p_message->token, p_item->token, p_item->token_len); + p_message->header.token_len = p_item->token_len; + + // Compiled away if COAP_ENABLE_OBSERVE_CLIENT is not set to 1. + coap_observe_client_response_handle(p_message, p_item); + + COAP_TRC(">> application callback"); + + COAP_MUTEX_UNLOCK(); + + if (is_ack(p_message)) + { + p_item->callback(NRF_SUCCESS, p_item->p_arg, p_message); + } + else + { + p_item->callback(COAP_TRANSMISSION_RESET_BY_PEER, p_item->p_arg, p_message); + } + + COAP_MUTEX_LOCK(); + + COAP_TRC("<< application callback"); + } + + COAP_TRC("Free mem, p_item->p_buffer = %p", p_item->p_buffer); + UNUSED_VARIABLE(nrf_free(p_item->p_buffer)); + + // Remove the queue element, as a match occured. + err_code = coap_queue_remove(p_item); + } + } + else if (is_response(p_message->header.code)) + { + COAP_TRC("CoAP message type: RESPONSE"); + + coap_queue_item_t * p_item; + err_code = coap_queue_item_by_token_get(&p_item, p_message->token, p_message->header.token_len); + if (err_code != NRF_SUCCESS) + { + // Compiled away if COAP_ENABLE_OBSERVE_CLIENT is not set to 1. + coap_observe_client_response_handle(p_message, NULL); + + UNUSED_VARIABLE(nrf_free((uint8_t *)p_message)); + + COAP_MUTEX_UNLOCK(); + + return err_code; + } + + if (p_item->callback != NULL) + { + // Compiled away if COAP_ENABLE_OBSERVE_CLIENT is not set to 1. + coap_observe_client_response_handle(p_message, p_item); + + COAP_TRC(">> application callback"); + + COAP_MUTEX_UNLOCK(); + + p_item->callback(NRF_SUCCESS, p_item->p_arg, p_message); + + COAP_MUTEX_LOCK(); + + COAP_TRC("<< application callback"); + } + + COAP_TRC("Free mem, p_item->p_buffer = %p", p_item->p_buffer); + UNUSED_VARIABLE(nrf_free(p_item->p_buffer)); + + err_code = coap_queue_remove(p_item); + + } + else if (is_request(p_message->header.code)) + { + COAP_TRC("CoAP message type: REQUEST"); + + if (m_request_handler != NULL) + { + uint32_t return_code = m_request_handler(p_message); + + // If success, then all processing and any responses has been sent + // by the application callback. + + // If not success, then send an appropriate error message back to the + // origin with the return_code from the callback. + if (return_code != NRF_SUCCESS) + { + if (return_code == NRF_ERROR_NOT_FOUND) + { + // Send response with provided CoAP code. + (void)send_error_response(p_message, COAP_CODE_404_NOT_FOUND); + } + else if (return_code == NRF_ERROR_NULL) + { + (void)send_error_response(p_message, COAP_CODE_405_METHOD_NOT_ALLOWED); + } + else + { + (void)send_error_response(p_message, COAP_CODE_400_BAD_REQUEST); + } + } + } + else + { + uint8_t * uri_pointers[COAP_RESOURCE_MAX_DEPTH] = {0, }; + + uint8_t uri_path_count = 0; + uint16_t index; + + for (index = 0; index < p_message->options_count; index++) + { + if (p_message->options[index].number == COAP_OPT_URI_PATH) + { + uri_pointers[uri_path_count++] = p_message->options[index].p_data; + } + } + + coap_resource_t * found_resource; + err_code = coap_resource_get(&found_resource, uri_pointers, uri_path_count); + + if (found_resource != NULL) + { + if (found_resource->callback != NULL) + { + if (((found_resource->permission) & (1 << ((p_message->header.code) - 1))) > 0) // Has permission for the requested CoAP method. + { + COAP_MUTEX_UNLOCK(); + + found_resource->callback(found_resource, p_message); + + COAP_MUTEX_LOCK(); + } + else + { + // Reply with Method Not Allowed. + err_code = send_error_response(p_message, COAP_CODE_405_METHOD_NOT_ALLOWED); + } + } + else + { + // Reply with Method Not Allowed. + err_code = send_error_response(p_message, COAP_CODE_405_METHOD_NOT_ALLOWED); + } + } + else + { + // Reply with NOT FOUND. + err_code = send_error_response(p_message, COAP_CODE_404_NOT_FOUND); + } + } + } + + COAP_TRC("Free mem, p_message = %p", (uint8_t *)p_message); + UNUSED_VARIABLE(nrf_free((uint8_t *)p_message)); + + COAP_EXIT(); + return err_code; +} + +uint32_t coap_message_send(uint32_t * p_handle, coap_message_t * p_message) +{ + COAP_MUTEX_LOCK(); + + uint32_t err_code = internal_coap_message_send(p_handle, p_message); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t coap_message_abort(uint32_t handle) +{ + + return NRF_ERROR_NOT_SUPPORTED; +} + +uint32_t coap_message_new(coap_message_t ** p_request, coap_message_conf_t * p_config) +{ + COAP_ENTRY(); + + uint32_t err_code; + + // If port is not configured, return error and skip initialization of the message. + if (p_config->port.port_number == 0) + { + return (NRF_ERROR_INVALID_PARAM | IOT_COAP_ERR_BASE); + } + + COAP_MUTEX_LOCK(); + + // Allocate space for a new message. + uint32_t size = sizeof(coap_message_t); + err_code = nrf_mem_reserve((uint8_t **)p_request, &size); + if (err_code != NRF_SUCCESS) + { + COAP_MUTEX_UNLOCK(); + return err_code; + } + + memset(*p_request, 0, sizeof(coap_message_t)); + COAP_TRC("Alloc mem, *p_request = %p", (uint8_t *)(*p_request)); + + // Allocate a scratch buffer for payload and options. + size = COAP_MESSAGE_DATA_MAX_SIZE; + err_code = nrf_mem_reserve(&((*p_request)->p_data), &size); + if (err_code != NRF_SUCCESS) + { + COAP_TRC("Allocation of message data buffer failed!"); + + COAP_TRC("Free mem, *p_request = %p", (uint8_t *)(*p_request)); + UNUSED_VARIABLE(nrf_free((uint8_t *)(*p_request))); + + COAP_MUTEX_UNLOCK(); + return err_code; + } + + memset((*p_request)->p_data, 0, size); + (*p_request)->data_len = size; + + COAP_TRC("Alloc mem, (*p_request)->p_data = %p", (uint8_t *)((*p_request)->p_data)); + + if (p_config->id == 0) // Message id is not set, generate one. + { + p_config->id = m_message_id_counter++; + } + + err_code = coap_message_create(*p_request, p_config); + + COAP_MUTEX_UNLOCK(); + + COAP_EXIT_WITH_RESULT(err_code); + + return err_code; +} + +uint32_t coap_message_delete(coap_message_t * p_message) +{ + COAP_ENTRY(); + + COAP_MUTEX_LOCK(); + + //If this is a request free the coap_message_t and the data buffer. + + COAP_TRC("Free mem, p_message->p_data = %p", p_message->p_data); + UNUSED_VARIABLE(nrf_free(p_message->p_data)); + + COAP_TRC("Free mem, p_message = %p", (uint8_t *)p_message); + UNUSED_VARIABLE(nrf_free((uint8_t *)p_message)); + + + COAP_MUTEX_UNLOCK(); + + COAP_EXIT(); + + return NRF_SUCCESS; +} + + +uint32_t coap_time_tick(void) +{ + COAP_MUTEX_LOCK(); + + coap_transport_process(); + + // Loop through the message queue to see if any packets needs retransmission, or has timed out. + coap_queue_item_t * p_item = NULL; + while (coap_queue_item_next_get(&p_item, p_item) == NRF_SUCCESS) + { + if (p_item->timeout == 0) + { + // If there is still retransmission attempts left. + if (p_item->retrans_count < COAP_MAX_RETRANSMIT_COUNT) + { + p_item->timeout = p_item->timeout_val * 2; + p_item->timeout_val = p_item->timeout; + p_item->retrans_count++; + + // Retransmit the message. + uint32_t err_code = coap_transport_write(&p_item->port, &p_item->remote, p_item->p_buffer, p_item->buffer_len); + if (err_code != NRF_SUCCESS) + { + app_error_notify(err_code, NULL); + } + } + + // No more retransmission attempts left, or max transmit span reached. + if ((p_item->timeout > COAP_MAX_TRANSMISSION_SPAN) || + (p_item->retrans_count >= COAP_MAX_RETRANSMIT_COUNT)) + { + + COAP_MUTEX_UNLOCK(); + + p_item->callback(COAP_TRANSMISSION_TIMEOUT, p_item->p_arg, NULL); + + COAP_MUTEX_LOCK(); + + COAP_TRC("Free mem, p_item->p_buffer = %p", p_item->p_buffer); + UNUSED_VARIABLE(nrf_free(p_item->p_buffer)); + + (void)coap_queue_remove(p_item); + } + } + else + { + p_item->timeout--; + } + } + + COAP_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + +uint32_t coap_request_handler_register(coap_request_handler_t p_request_handler) +{ + COAP_MUTEX_LOCK(); + + m_request_handler = p_request_handler; + + COAP_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + +__WEAK void coap_transport_input(void) +{ + // By default not implemented. Transport specific. +} + +void coap_input(void) +{ + COAP_MUTEX_LOCK(); + + coap_transport_input(); + + COAP_MUTEX_UNLOCK(); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap.h new file mode 100644 index 0000000..09d9ecb --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap.h @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap.h + * + * @defgroup iot_sdk_coap_api CoAP interface + * @ingroup iot_sdk_coap + * @{ + * @brief Interface for the CoAP protocol. + */ + +#ifndef COAP_H__ +#define COAP_H__ + +#include "iot_errors.h" +#include "coap_api.h" +#include "sdk_os.h" + +/** + * @defgroup iot_coap_log Module's Log Macros + * @details Macros used for creating module logs which can be useful in understanding handling + * of events or actions on API requests. These are intended for debugging purposes and + * can be enabled by defining the COAP_ENABLE_LOGS to 1. + * @note If NRF_LOG_ENABLED is disabled, having COAP_ENABLE_LOGS has no effect. + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if (COAP_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_COAP_ERR_BASE); \ + } + +/**@brief Verify that parameter members has been set by the application. */ +#define NULL_PARAM_MEMBER_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_COAP_ERR_BASE); \ + } +#else + +#define NULL_PARAM_CHECK(PARAM) +#define NULL_PARAM_MEMBER_CHECK(PARAM) + +#endif // COAP_DISABLE_API_PARAM_CHECK + +/** + * @defgroup iot_coap_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case the need to use an alternative architecture arises. + * @{ + */ +#define COAP_MUTEX_LOCK() SDK_MUTEX_LOCK(m_coap_mutex) /**< Lock module using mutex */ +#define COAP_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_coap_mutex) /**< Unlock module using mutex */ + +SDK_MUTEX_DEFINE(m_coap_mutex) + +/** @} */ + +/**@brief Sends a CoAP message. + * + * @details Sends out a request using the underlying transport layer. Before sending, the + * \ref coap_message_t structure is serialized and added to an internal message queue + * in the library. The handle returned can be used to abort the message from being + * retransmitted at any time. + * + * @param[out] p_handle Handle to the message if CoAP CON/ACK messages has been used. Returned + * by reference. + * @param[in] p_message Message to be sent. + * + * @retval NRF_SUCCESS If the message was successfully encoded and scheduled for transmission. + */ +uint32_t internal_coap_message_send(uint32_t * p_handle, coap_message_t * p_message); + +#ifdef __cplusplus +} +#endif + +#endif // COAP_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_api.h new file mode 100644 index 0000000..9bc0222 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_api.h @@ -0,0 +1,663 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap_api.h + * + * @defgroup iot_sdk_coap_api CoAP Application Programming Interface + * @ingroup iot_sdk_coap + * @{ + * @brief Public API of Nordic's CoAP implementation. + * + */ + +#ifndef COAP_API_H__ +#define COAP_API_H__ + +#include <stdint.h> +#include "coap_transport.h" +#include "coap_codes.h" +#include "sdk_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@defgroup COAP_CONTENT_TYPE_MASK Resource content type bitmask values + * @{ */ +#define COAP_CT_MASK_PLAIN_TEXT 0x01 /**< Content type Plain text supported in the endpoint resource. */ +#define COAP_CT_MASK_CHARSET_UTF8 0x02 /**< Content type Charset-UTF8 supported in the endpoint resource. */ +#define COAP_CT_MASK_APP_LINK_FORMAT 0x04 /**< Content type Application/link-format supported in the endpoint resource. */ +#define COAP_CT_MASK_APP_XML 0x08 /**< Content type Application/xml supported in the endpoint resource. */ +#define COAP_CT_MASK_APP_OCTET_STREAM 0x10 /**< Content type Application/octet-stream supported in the endpoint resource. */ +#define COAP_CT_MASK_APP_EXI 0x20 /**< Content type Application/exi supported in the endpoint resource. */ +#define COAP_CT_MASK_APP_JSON 0x40 /**< Content type Application/json supported in the endpoint resource. */ +/**@} */ + +/**@defgroup COAP_METHOD_PERMISSION Resource method permission bitmask values + * @{ */ +#define COAP_PERM_NONE 0x0000 /**< Permission by default. Do not allow any method in the COAP/COAPS endpoint resource. */ +#define COAP_PERM_GET 0x0001 /**< Permission to allow GET method in the COAP endpoint resource. */ +#define COAP_PERM_POST 0x0002 /**< Permission to allow POST method in the COAP endpoint resource. */ +#define COAP_PERM_PUT 0x0004 /**< Permission to allow PUT method in the COAP endpoint resource. */ +#define COAP_PERM_DELETE 0x0008 /**< Permission to allow DELETE method in the COAP endpoint resource. */ +#define COAPS_PERM_GET 0x0010 /**< Permission to allow GET method in the COAPS endpoint resource. */ +#define COAPS_PERM_POST 0x0020 /**< Permission to allow POST method in the COAPS endpoint resource. */ +#define COAPS_PERM_PUT 0x0040 /**< Permission to allow PUT method in the COAPS endpoint resource. */ +#define COAPS_PERM_DELETE 0x0080 /**< Permission to allow DELETE method in the COAPS endpoint resource. */ +#define COAP_PERM_OBSERVE 0x0100 /**< Permission to allow OBSERVE of the endpoint resource. */ +/**@} */ + +/**@cond */ +// Forward declare structs. +typedef struct coap_message_t coap_message_t; +typedef struct coap_resource_t coap_resource_t; +/**@endcond */ + +/**@brief Callback function to call upon undefined behaviour. + * + * @param[in] error_code Error code from CoAP module. + * @param[in] p_message CoAP message processed when error ocoured. Could be NULL. + */ +typedef void (*coap_error_callback_t)(uint32_t error_code, coap_message_t * p_message); + +/**@brief Callback function to be registered with CoAP messages. + * + * @param[in] status Response status. Possible status codes: + * NRF_SUCCESS If response was successfully received, + * COAP_TRANSMISSION_RESET_BY_PEER if a reset response was recieved or, + * COAP_TRANSMISSION_TIMEOUT if transmission + * @param[in] p_arg Miscellaneous pointer to application provided data that is associated with + * the message. + * @param[in] p_message Pointer to a CoAP Response message. + */ +typedef void (*coap_response_callback_t)(uint32_t status, void * p_arg, coap_message_t * p_message); + +/**@brief Handler function for manually handling all incoming requests. + * + * @details If the function is set, the error code given back will trigger error messages + * to be sent back by CoAP to indicate failure. Default error message will be 4.00 + * BAD REQUEST. On success, it is expected that the callback has already sent a + * response message. + * + * @param[in] p_request Pointer to a CoAP Request message. + * + * @retval NRF_SUCCESS If the message was successfully has been handled. + * @retval NRF_ERROR_NOT_FOUND If the message did not match any recognized resources, and a + * 4.04 NOT FOUND error message should be sent back to the requester. + * @retval NRF_ERROR_NULL If the message resolved the resource and operation not permitted, + * and a 4.05 METHOD NOT ALLOWED error message should be sent back to + * the reqester. + * + */ +typedef uint32_t (*coap_request_handler_t)(coap_message_t * p_request); + +#ifdef COAP_AUTOMODE + +/**@brief Callback function to be registered with CoAP endpoint resources. in auto-mode. + * + * @details The callback needs to implement any action based on the request. The p_response message + * will automatically be sent as response when the callback function returns. The memory + * is allocated by the caller, so the application does not have to free up the memory used + * for the response. + * + * @param[in] p_resource Pointer to the request message's target resource. + * @param[in] p_request Pointer to the request message. + * @param[out] p_response Pointer to the prepared response message. The Application can override + * its values. + */ +typedef void (*coap_method_callback_t) (coap_resource_t * p_resource, coap_message_t * p_request, coap_message_t * p_response); + +#else // COAP_AUTOMODE + +/**@brief Callback function to be registered with CoAP endpoint resources. in auto-mode. + * + * @details The callback needs to implement any action based on the request. The callback is + * responsible of handling the sending of any response back to the requester. The memory + * for p_request will be freed up by the coap module after the callback has been + * completed. + * + * @param[in] p_resource Pointer to the request message's target resource. + * @param[in] p_request Pointer to the request message. + */ +typedef void (*coap_method_callback_t) (coap_resource_t * p_resource, coap_message_t * p_request); + +#endif // COAP_AUTOMODE + +/**@brief Enumeration of CoAP content types. */ +typedef enum +{ + COAP_CT_PLAIN_TEXT = 0, /**< Plain text content format number. Default. */ + COAP_CT_APP_LINK_FORMAT = 40, /**< Application/link-format content format number. */ + COAP_CT_APP_XML = 41, /**< Application/xml content format number. */ + COAP_CT_APP_OCTET_STREAM = 42, /**< Application/octet-stream content format number. */ + COAP_CT_APP_EXI = 47, /**< Application/exi content format number. */ + COAP_CT_APP_JSON = 50 /**< Application/json content format number. */ +} coap_content_type_t; + +/**@brief Enumeration of CoAP options numbers. */ + +#define COAP_OPT_RESERVED0 0 /**< Reserved option number. */ +#define COAP_OPT_IF_MATCH 1 /**< If-Match option number. */ +#define COAP_OPT_URI_HOST 3 /**< URI-Host option number. */ +#define COAP_OPT_ETAG 4 /**< ETag option number. */ +#define COAP_OPT_IF_NONE_MATCH 5 /**< If-None-Match option number. */ +#define COAP_OPT_URI_PORT 7 /**< URI-Port option number. */ +#define COAP_OPT_LOCATION_PATH 8 /**< Location-Path option number. */ +#define COAP_OPT_URI_PATH 11 /**< URI-Path option number. */ +#define COAP_OPT_CONTENT_FORMAT 12 /**< Content-Format option number. */ +#define COAP_OPT_MAX_AGE 14 /**< Max-Age option number. */ +#define COAP_OPT_URI_QUERY 15 /**< URI-Query option number. */ +#define COAP_OPT_ACCEPT 17 /**< Accept option number. */ +#define COAP_OPT_LOCATION_QUERY 20 /**< Location-Query option number. */ +#define COAP_OPT_BLOCK2 23 /**< Block2 option number. */ +#define COAP_OPT_BLOCK1 27 /**< Block1 option number. */ +#define COAP_OPT_SIZE2 28 /**< Size2 option number. */ +#define COAP_OPT_PROXY_URI 35 /**< Proxy-URI option number. */ +#define COAP_OPT_PROXY_SCHEME 39 /**< Proxy-Scheme option number. */ +#define COAP_OPT_SIZE1 60 /**< Size1 option number. */ +#define COAP_OPT_RESERVED1 128 /**< Reserved option number. */ +#define COAP_OPT_RESERVED2 132 /**< Reserved option number. */ +#define COAP_OPT_RESERVED3 136 /**< Reserved option number. */ +#define COAP_OPT_RESERVED4 140 /**< Reserved option number. */ + + +/**@brief Enumeration of CoAP message types. */ +typedef enum +{ + COAP_TYPE_CON = 0, /**< Confirmable Message type. */ + COAP_TYPE_NON, /**< Non-Confirmable Message type. */ + COAP_TYPE_ACK, /**< Acknowledge Message type. */ + COAP_TYPE_RST /**< Reset Message type. */ +} coap_msg_type_t; + +/**@brief Structure to hold a CoAP option. + */ +typedef struct +{ + uint16_t number; /**< Option number (including the extended delta value if any). */ + uint16_t length; /**< Option length (including the extended length value in any). */ + uint8_t * p_data; /**< Pointer to the memory where the data of the option is located. */ +} coap_option_t; + + + +/**@brief Structure to hold a CoAP message configuration. + * + * @details The structure is used when calling the \ref coap_message_new API function. + * All data supplied will be copied to the created message. + */ +typedef struct +{ + coap_response_callback_t response_callback; /**< Callback function to be called when a response matching the token is identified. */ + uint8_t token[8]; /**< Message token. token_len must be set to indicate how many of the bytes should be used in the token. */ + uint8_t token_len; /**< Token size in bytes. */ + uint16_t id; /**< Message ID. If 0 is given, the library will replace this number with an autogenerated value. */ + coap_msg_type_t type; /**< Message type: COAP_TYPE_CON, COAP_TYPE_NON, COAP_TYPE_ACK, or COAP_TYPE_RST. */ + coap_msg_code_t code; /**< Message code (definitions found in coap_msg_code_t). */ + coap_port_t port; /**< Transport layer variable to associate the message with an underlying Transport Layer socket descriptor. */ +} coap_message_conf_t; + +/**@brief Structure to hold a CoAP message header. + * + * @details This structure holds the 4-byte mandatory CoAP header. The structure uses bitfields + * to save memory footprint. + */ +typedef struct +{ + uint8_t version :2; /**< CoAP version number. The current specification RFC7252 mandates this to be version 1. The version number can be modified in sdk_config.h. */ + uint8_t type :2; /**< Message type: COAP_TYPE_CON, COAP_TYPE_NON, COAP_TYPE_ACK, or COAP_TYPE_RST. */ + uint8_t token_len :4; /**< Length of the message token. */ + uint8_t code; /**< Message code (definitions found in @ref coap_msg_code_t). */ + uint16_t id; /**< Message ID in little-endian format. Convertion to Network Byte Order will be handled by the library. */ +} coap_message_header_t; + +/**@brief Structure to hold a CoAP message. + * + * @details The CoAP message structure contains both internal and public members to + * serialize and deserialize data supplied from the application to a byte buffer sent + * over UDP. The message structure is used both in transmission and reception, which + * makes it easy to handle in an application. Updating the message should be done + * using the provided APIs, not by manually assigning new values to the members directly. + * Reading the members, on the other hand, is fine. + */ +struct coap_message_t +{ + coap_remote_t remote; /**< Public. Structure containing address information and port number to the remote. */ + coap_remote_t local; /**< Public. Structure containing local destination address information and port number. */ + coap_message_header_t header; /**< Public. Header structure containing the mandatory CoAP 4-byte header fields. */ + uint8_t * p_payload; /**< Public. Pointer to the payload buffer in the message. */ + uint16_t payload_len; /**< Public. Size of the payload in the message. */ + uint8_t options_count; /**< Public. The number of options in the message. */ + coap_option_t options[COAP_MAX_NUMBER_OF_OPTIONS]; /**< Public. Array options in the message. */ + void * p_arg; /**< Public. Miscellaneous pointer to application provided data that is associated with the message. */ + + coap_response_callback_t response_callback; /**< Internal. Function callback set by the application to be called when a response to this message is received. Should be set by the application through a configuration parameter. */ + uint8_t token[8]; /**< Internal. Array holding the variable-sized message token. Should be set by the application through a configuration parameter. */ + coap_port_t port; /**< Internal. Transport layer variable to associate the message with an underlying Transport Layer socket descriptor. */ + uint16_t options_len; /**< Internal. Length of the options including the mandatory header with extension bytes and option data. Accumulated every time a new options is added. */ + uint16_t options_offset; /**< Internal. Index to where the next option or payload can be added in the message's data buffer */ + uint16_t options_delta; /**< Internal. Current option number. Used to calculate the next option delta when adding new options to the message. */ + uint8_t * p_data; /**< Internal. Data buffer for adding dynamically sized options and payload. */ + uint16_t data_len; /**< Internal. Length of the provided data buffer for options and payload. */ +}; + + +/**@brief Structure to hold a CoAP endpoint resource. +*/ +struct coap_resource_t +{ + uint8_t child_count; /**< Number of children in the linked list. */ + uint16_t permission; /**< Bitmask to tell which methods are permitted on the resource. Bit values available can be seen in \ref COAP_METHOD_PERMISSION. */ + coap_resource_t * p_sibling; /**< Sibling pointer to the next element in the list. */ + coap_resource_t * p_front; /**< Pointer to the beginning of the linked list. */ + coap_resource_t * p_tail; /**< Pointer to the last added child in the list. */ + coap_method_callback_t callback; /**< Callback to the resource handler. */ + uint32_t ct_support_mask; /**< Bitmask to tell which content types are supported by the resource. Bit values available can be seen in \ref COAP_CONTENT_TYPE_MASK. */ + uint32_t max_age; /**< Max age of resource endpoint value. */ + uint32_t expire_time; /**< Number of seconds until expire. */ + char name[COAP_RESOURCE_MAX_NAME_LEN+1]; /**< Name of the resource. Must be zero terminated. */ +}; + +/**@brief Initializes the CoAP module. + * + * @details Initializes the library module and resets internal queues and resource registrations. + * + * @param[in] token_rand_seed Random number seed to be used to generate the token numbers. + * @param[in] p_transport_params Pointer to transport parameters. Providing the list of ports + * to be used by CoAP. + * + * @retval NRF_SUCCESS If initialization succeeded. + */ +uint32_t coap_init(uint32_t token_rand_seed, coap_transport_init_t * p_transport_params); + +/**@brief Register error handler callback to the CoAP module. + * + * @param[in] error_callback Function to be called upon unknown messages and failure. + * + * @retval NRF_SUCCESS If registration was successful. + */ +uint32_t coap_error_handler_register(coap_error_callback_t error_callback); + +/**@brief Register request handler which should handle all incoming requests. + * + * @details Setting this request handler redirects all requests to the application provided + * callback routine. The callback handler might be cleared by NULL, making coap + * module handle the requests and do resource lookup in order to process the + * requests. + * + * @param[in] p_request_handler Function pointer to the provided request handler. + * + * @retval NRF_SUCCESS If registration was successful. + */ +uint32_t coap_request_handler_register(coap_request_handler_t p_request_handler); + +/**@brief Sends a CoAP message. + * + * @details Sends out a request using the underlying transport layer. Before sending, the + * \ref coap_message_t structure is serialized and added to an internal message queue + * in the library. The handle returned can be used to abort the message from being + * retransmitted at any time. + * + * @param[out] p_handle Handle to the message if CoAP CON/ACK messages has been used. Returned + * by reference. + * @param[in] p_message Message to be sent. + * + * @retval NRF_SUCCESS If the message was successfully encoded and scheduled for transmission. + */ +uint32_t coap_message_send(uint32_t * p_handle, coap_message_t * p_message); + +/**@brief Abort a CoAP message. + * + * @details Aborts an ongoing transmission. If the message has not yet been sent, it will be + * deleted from the message queue as well as stop any ongoing re-transmission of the + * message. + * + * @param[in] handle Handle of the message to abort. + * + * @retval NRF_SUCCESS If the message was successfully aborted and removed from the + * message queue. + * @retval NRF_ERROR_NOT_FOUND If the message with the given handle was not located in the + * message queue. + */ +uint32_t coap_message_abort(uint32_t handle); + +/**@brief Creates CoAP message, initializes, and allocates the needed memory. + * + * @details Creates a CoAP message. This is an intermediate representation of the message, + * because the message will be serialized by the library before it is transmitted. The structure + * is verbose to facilitate configuring the message. Options, payload, and + * remote address information can be added using API function calls. + * + * @param[inout] p_request Pointer to be filled by the allocated CoAP message structures. + * @param[in] p_config Configuration for the message to be created. Manual configuration + * can be performed after the message creation, except for the CLIENT port + * association. + * + * @retval NRF_SUCCESS If the request was successfully allocated and initialized. + * @retval NRF_ERROR_INVALID_PARAM If local port number was not configured. + */ +uint32_t coap_message_new(coap_message_t ** p_request, coap_message_conf_t * p_config); + +/**@brief Deletes the CoAP request message. + * + * @details Frees up memory associated with the request message. + * + * @param[in] p_message Pointer to the request message to delete. + */ +uint32_t coap_message_delete(coap_message_t * p_message); + +/**@brief Adds a payload to a CoAP message. + * + * @details Sets a data payload to a request or response message. + * + * This function must be called after all CoAP options have been added. + * Due to internal buffers in the library, the payload will be added after any options + * in the buffer. If an option is added after the payload, this option will over-write + * the payload in the internal buffer. + * + * @param[inout] p_message Pointer to the message to add the payload to. + * @param[in] p_payload Pointer to the payload to be added. + * @param[in] payload_len Size of the payload to be added. + * + * @retval NRF_SUCCESS If the payload was successfully added to the message. + * @retval NRF_ERROR_NO_MEM If the payload could not fit within the allocated payload memory + * defined by sdk_config.h COAP_MESSAGE_DATA_MAX_SIZE. + */ +uint32_t coap_message_payload_set(coap_message_t * p_message, + void * p_payload, + uint16_t payload_len); + +/**@brief Adds an empty CoAP option to the message. + * + * Option numbers must be in ascending order, adding the one with the smallest number + * first and greatest last. If the order is incorrect, the delta number calculation will + * result in an invalid or wrong delta number for the option. + * + * @param[inout] p_message Pointer to the message to add the option to. Should not be NULL. + * @param[in] option_num The option number to add to the message. + * + * @retval NRF_SUCCESS If the empty option was successfully added to the message. + * @retval NRF_ERROR_DATA_SIZE If the data exceeds the available message buffer data size. + * @retval NRF_ERROR_NO_MEM If the maximum number of options that can be added to a message has been reached. + */ +uint32_t coap_message_opt_empty_add(coap_message_t * p_message, uint16_t option_num); + +/**@brief Adds a uint CoAP option to the message. + * + * Option numbers must be in ascending order, adding the one with the smallest number + * first and greatest last. If the order is incorrect, the delta number calculation will + * result in an invalid or wrong delta number for the option. + * + * @param[inout] p_message Pointer to the message to add the option to. Should not be NULL. + * @param[in] option_num The option number to add to the message. + * @param[in] data An unsigned value (8-bit, 16-bit, or 32-bit) casted to uint32_t. + * The value of the data is used to determine how many bytes + * CoAP must use to represent this option value. + * + * @retval NRF_SUCCESS If the unsigned integer option was successfully added to the message. + * @retval NRF_ERROR_DATA_SIZE If the data exceeds the available message buffer data size. + * @retval NRF_ERROR_NO_MEM If the maximum number of options that can be added to a message has been reached. + */ +uint32_t coap_message_opt_uint_add(coap_message_t * p_message, uint16_t option_num, uint32_t data); + +/**@brief Adds a string CoAP option to the message. + * + * Option numbers must be in ascending order, adding the one with the smallest number + * first and greatest last. If the order is incorrect, the delta number calculation will + * result in an invalid or wrong delta number for the option. + * + * @param[inout] p_message Pointer to the message to add the option to. Should not be NULL. + * @param[in] option_num The option number to add to the message. + * @param[in] p_data Pointer to a string buffer to be used as value for the option. + * Should not be NULL. + * @param[in] length Length of the string buffer provided. + * + * @retval NRF_SUCCESS If the string option was successfully added to the message. + * @retval NRF_ERROR_DATA_SIZE If the data exceeds the available message buffer data size. + * @retval NRF_ERROR_NO_MEM If the maximum number of options that can be added to a message has been reached. + */ +uint32_t coap_message_opt_str_add(coap_message_t * p_message, uint16_t option_num, uint8_t * p_data, uint16_t length); + +/**@brief Adds an opaque CoAP option to the message. + * + * Option numbers must be in ascending order, adding the one with the smallest number + * first and greatest last. If the order is incorrect, the delta number calculation will + * result in an invalid or wrong delta number for the option. + * + * @param[inout] p_message Pointer to the message to add the option to. Should not be NULL. + * @param[in] option_num The option number to add to the message. + * @param[in] p_data Pointer to an opaque buffer to be used as value for the option. + * Should not be NULL. + * @param[in] length Length of the opaque buffer provided. + * + * @retval NRF_SUCCESS If the opaque option was successfully added to the message. + * @retval NRF_ERROR_DATA_SIZE If the data exceeds the available message buffer data size. + * @retval NRF_ERROR_NO_MEM If the maximum number of options that can be added to a message has been reached. + */ +uint32_t coap_message_opt_opaque_add(coap_message_t * p_message, uint16_t option_num, uint8_t * p_data, uint16_t length); + +/**@brief Sets a remote address and port number to a CoAP message. + * + * @details Copies the content of the provided pointer into the CoAP message. + * + * @param[inout] p_message Pointer to the message to add the address information to. + * Should not be NULL. + * @param[in] p_address Pointer to a structure holding the address information for the remote server or client. + * Should not be NULL. + * + * @retval NRF_SUCCESS When copying the content has finished. + */ +uint32_t coap_message_remote_addr_set(coap_message_t * p_message, coap_remote_t * p_address); + +/**@brief Creates a CoAP endpoint resource. + * + * @details Initializes the \ref coap_resource_t members. + * + * The first resource that is created will be set as the root of the resource + * hierarchy. + * + * @param[in] p_resource Pointer to coap_resource_t passed in by reference. + * This variable must be stored in non-volatile memory. + * Should not be NULL. + * @param[in] name Verbose name of the service (zero-terminated + * string). The maximum length of a name is defined + * by COAP_RESOURCE_MAX_NAME_LEN in @c sdk_config.h + * and can be adjusted if needed. Should not be NULL. + * @retval NRF_ERROR_DATA_SIZE If the provided name is larger than the available name buffer. + * @retval NRF_ERROR_NULL If the pointer to the resource or the provided + * name buffer is NULL. + */ +uint32_t coap_resource_create(coap_resource_t * p_resource, const char * name); + +/**@brief Adds a child resource. + * + * @details The hierarchy is constructed as a linked list with a maximum number of children. + * COAP_RESOURCE_MAX_DEPTH in @c sdk_config.h sets the maximum depth. The maximum + * number of children can be adjusted if more levels are needed. + * + * @param[in] p_parent Resource to attach the child to. Should not be NULL. + * @param[in] p_child Child resource to attach. Should not be NULL. + * + * @retval NRF_SUCCESS If the child was successfully added. + * @retval COAP_ERROR_MAX_DEPTH_REACHED If the child is exceeding the maximum depth defined. + */ +uint32_t coap_resource_child_add(coap_resource_t * p_parent, coap_resource_t * p_child); + +/**@brief Generates .well-known/core string. + * + * @details This is a helper function for generating a CoRE link-format encoded string used for + * CoAP discovery. The function traverse the resource hierarchy recursively. + * The result will be resources listed in link-format. This function can be called when + * all resources have been added by the application. + * + * @param[inout] string Buffer to use for the .well-known/core string. Should not be NULL. + * @param[inout] length Length of the string buffer. Returns the used number of bytes from + * the provided buffer. + * + * @retval NRF_SUCCESS If string generation was successful. + * @retval NRF_ERROR_NULL If the string buffer was a NULL pointer. + * @retval NRF_ERROR_DATA_SIZE If the size of the generated string exceeds the given buffer size. + * @retval NRF_ERROR_INVALID_STATE If no resource has been registered. + */ +uint32_t coap_resource_well_known_generate(uint8_t * string, uint16_t * length); + +/**@brief Get the root resource pointer. + * + * @param[out] pp_resource Pointer to be filled with pointer to the root resource. + * + * @retval NRF_SUCCESS If root resource was located. + * @retval NRF_ERROR_NOT_FOUND If root resource was not located. + * @retval NRF_ERROR_NULL If output pointer was NULL. + */ +uint32_t coap_resource_root_get(coap_resource_t ** pp_resource); + +/**@brief Check whether a message contains a given CoAP Option. + * + * @param[in] p_message Pointer to the to check for the CoAP Option. + * Should not be NULL. + * @param[in] option CoAP Option to check for in the CoAP message. + * + * @retval NRF_SUCCESS If the CoAP Option is present in the message. + * @retval NRF_ERROR_NULL If the pointer to the message is NULL. + * @retval NRF_ERROR_NOT_FOUND If the CoAP Option is not present in the message. + */ +uint32_t coap_message_opt_present(coap_message_t * p_message, uint16_t option); + +/**@brief Check whether a message contains a given CoAP Option and return the index of the entry + * in the message option list. + * + * @param[in] p_index Value by reference to fill the resolved index into. Should not be NULL. + * @param[in] p_message Pointer to the to check for the CoAP Option. + * Should not be NULL. + * @param[in] option CoAP Option to check for in the CoAP message. + * + * @retval NRF_SUCCESS If the CoAP Option is present in the message. + * @retval NRF_ERROR_NULL If the pointer to the message or the p_index is NULL. + * @retval NRF_ERROR_NOT_FOUND If the CoAP Option is not present in the message. + */ +uint32_t coap_message_opt_index_get(uint8_t * p_index, coap_message_t * p_message, uint16_t option); + +/**@brief Find common content type between the CoAP message and the resource. + * + * @details The return value will be the first match between the ACCEPT options and the supported + * content types in the resource. The priority is by content-format ID starting going + * from the lowest value to the highest. + * + * @param[out] p_ct Resolved content type given by reference. Should not be NULL. + * @param[in] p_message Pointer to the message. Should not be NULL. + * @param[in] p_resource Pointer to the resource. Should not be NULL. + * + * @retval NRF_SUCCESS If match was found. + * @retval NRF_ERROR_NOT_FOUND If no match was found. + */ +uint32_t coap_message_ct_match_select(coap_content_type_t * p_ct, coap_message_t * p_message, coap_resource_t * p_resource); + +/**@brief CoAP time tick used for retransmitting any message in the queue if needed. + * + * @retval NRF_SUCCESS If time tick update was successfully handled. + */ +uint32_t coap_time_tick(void); + +#if (COAP_DISABLE_DTLS_API == 0) +/**@brief Setup secure DTLS session. + * + * @details For the client role, this API triggers a DTLS handshake. Until the handshake is complete + * with the remote, \ref coap_message_send will fail. + * For the server role, this API does not create any DTLS session. A DTLS session is + * created each time a new client remote endpoint sends a request on the local port of the + * server. + * + * @note The success of this function does not imply that the DTLS handshake is successfull. + * + * @note Only one DTLS session is permitted between a local and remote endpoint. Therefore, in case + * a DTLS session was established between the local and remote endpoint, the existing DTLS + * session will be reused irrespective of the role and number of times this API was called. + * In case the application desires a fresh security setup, it must first call the + * \ref coap_security_destroy to tear down the existing setup. + * + * @param[in] local_port Local port to bind the session to. + * @param[in] role Role of the session. DTLS server or client defined in the enumeration + * \ref nrf_tls_role_t. + * @param[in] p_remote Pointer to a structure holding the address information for the remote + * endpoint. If a the device is acting as a server, a NULL pointer shall be + * given as a parameter. Rationale: The server is not envisioned to be + * bound a pre-known client endpoint. Therefore, security server settings + * shall be setup irrespective of the remote client. + * @param[in] p_settings Pointer to a structure holding the DTLS credentials. + * + * @retval NRF_SUCCESS If setup of the secure DTLS session was successfull. + */ +uint32_t coap_security_setup(uint16_t local_port, + nrf_tls_role_t role, + coap_remote_t * const p_remote, + nrf_tls_key_settings_t * const p_settings); + + +/**@brief Destroy a secure DTLS session. + * + * @details Terminate and clean up any session associated with the local port and the remote. + * + * @param[in] local_port Local port to unbind the session from. + * @param[in] p_remote Pointer to a structure holding the address information for the remote + * endpoint. Providing a NULL as p_remote will clean up all DTLS sessions + * associated with the local port. + * + * @retval NRF_SUCCESS If the destruction of the secure DTLS session was successfull. + */ +uint32_t coap_security_destroy(uint16_t local_port, + coap_remote_t * const p_remote); + +#endif // COAP_DISABLE_DTLS_API + +/**@brief Process loop when using coap BSD socket transport implementation. + * + * @details This is blocking call. The function unblock is only + * triggered upon an socket event registered to select() by coap transport. + * This function must be called as often as possible in order to dispatch incoming + * socket events. Preferred to be put in the application's main loop or similar. + **/ +void coap_input(void); + +#ifdef __cplusplus +} +#endif + +#endif // COAP_API_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_block.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_block.c new file mode 100644 index 0000000..f915f54 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_block.c @@ -0,0 +1,203 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdbool.h> +#include <stddef.h> + +#include "coap_block.h" +#include "nrf_error.h" +#include "iot_errors.h" +#include "sdk_config.h" + + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * COAP_DISABLE_API_PARAM_CHECK should be defined to disable these checks. + * + * @{ + */ +#if (COAP_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_COAP_ERR_BASE); \ + } +#else // COAP_DISABLE_API_PARAM_CHECK + +#define NULL_PARAM_CHECK(PARAM) + +#endif // COAP_DISABLE_API_PARAM_CHECK +/** @} */ + + +#define BLOCK_SIZE_BASE_CONSTANT 4 /**< Block size base exponent. 4 means a base block size of 2^4 = 16 bytes. */ + +#define BLOCK_SIZE_16 0 /**< Block size of 2^(4+0) = 16 bytes. */ +#define BLOCK_SIZE_32 1 /**< Block size of 2^(4+1) = 32 bytes. */ +#define BLOCK_SIZE_64 2 /**< Block size of 2^(4+2) = 64 bytes. */ +#define BLOCK_SIZE_128 3 /**< Block size of 2^(4+3) = 128 bytes. */ +#define BLOCK_SIZE_256 4 /**< Block size of 2^(4+4) = 256 bytes. */ +#define BLOCK_SIZE_512 5 /**< Block size of 2^(4+5) = 512 bytes. */ +#define BLOCK_SIZE_1024 6 /**< Block size of 2^(4+6) = 1024 bytes. */ +#define BLOCK_SIZE_2048_RESERVED 7 /**< Reserved. */ + +#define BLOCK_MORE_BIT_UNSET 0 /**< Value when more flag is set. */ +#define BLOCK_MORE_BIT_SET 1 /**< Value when more flag is not set. */ + +#define BLOCK_SIZE_POS 0 /**< Bit offset to the size. */ +#define BLOCK_MORE_BIT_POS 3 /**< Bit offset to the more bit. */ +#define BLOCK_NUMBER_POS 4 /**< Bit offset to the block number. */ + +#define BLOCK_SIZE_MASK 0x7 /**< Block size mask. */ +#define BLOCK_MORE_BIT_MASK (1 << BLOCK_MORE_BIT_POS) /**< More bit mask. */ +#define BLOCK_NUMBER_MASK (0xFFFFF << 4) /**< Block number mask. */ + +#define BLOCK_SIZE_MAX 0x7 /**< Maximum block size number. */ +#define BLOCK_MORE_BIT_MAX BLOCK_MORE_BIT_SET /**< Maximum more bit value. */ +#define BLOCK_NUMBER_MAX 0xFFFFF /**< Maximum block number. 20 bits max value is (1 << 20) - 1. */ + +static uint32_t block_opt_encode(uint8_t more, + uint16_t size, + uint32_t number, + uint32_t * p_encoded) +{ + if ((number > BLOCK_NUMBER_MAX) || (more > BLOCK_MORE_BIT_MAX)) + { + return (NRF_ERROR_INVALID_PARAM | IOT_COAP_ERR_BASE); + } + + uint32_t val = 0; + + switch (size) + { + case 16: + val = BLOCK_SIZE_16; + break; + + case 32: + val = BLOCK_SIZE_32; + break; + + case 64: + val = BLOCK_SIZE_64; + break; + + case 128: + val = BLOCK_SIZE_128; + break; + + case 256: + val = BLOCK_SIZE_256; + break; + + case 512: + val = BLOCK_SIZE_512; + break; + + case 1024: + val = BLOCK_SIZE_1024; + break; + + case 2048: + // Falltrough. + default: + // Break omitted. + return (NRF_ERROR_INVALID_PARAM | IOT_COAP_ERR_BASE); + } + + // Value has been initialized. + val |= (more) << BLOCK_MORE_BIT_POS; + val |= (number) << BLOCK_NUMBER_POS; + + *p_encoded = val; + return NRF_SUCCESS; +} + +static uint32_t block_opt_decode(uint32_t encoded, + uint8_t * p_more, + uint16_t * p_size, + uint32_t * p_number) +{ + if ((encoded & BLOCK_SIZE_MASK) == BLOCK_SIZE_2048_RESERVED) + { + return (NRF_ERROR_INVALID_DATA | IOT_COAP_ERR_BASE); + } + + if ((encoded >> BLOCK_NUMBER_POS) > BLOCK_NUMBER_MAX) + { + return (NRF_ERROR_INVALID_PARAM | IOT_COAP_ERR_BASE); + } + + *p_size = (1 << ((BLOCK_SIZE_BASE_CONSTANT + (encoded & BLOCK_SIZE_MASK)))); + *p_more = (encoded & BLOCK_MORE_BIT_MASK) >> BLOCK_MORE_BIT_POS; + *p_number = (encoded & BLOCK_NUMBER_MASK) >> BLOCK_NUMBER_POS; + + return NRF_SUCCESS; +} + +uint32_t coap_block_opt_block1_encode(uint32_t * p_encoded, coap_block_opt_block1_t * p_opt) +{ + NULL_PARAM_CHECK(p_encoded); + NULL_PARAM_CHECK(p_opt); + return block_opt_encode(p_opt->more, p_opt->size, p_opt->number, p_encoded); +} + +uint32_t coap_block_opt_block1_decode(coap_block_opt_block1_t * p_opt, uint32_t encoded) +{ + NULL_PARAM_CHECK(p_opt); + return block_opt_decode(encoded, &p_opt->more, &p_opt->size, &p_opt->number); +} + +uint32_t coap_block_opt_block2_encode(uint32_t * p_encoded, coap_block_opt_block2_t * p_opt) +{ + NULL_PARAM_CHECK(p_encoded); + NULL_PARAM_CHECK(p_opt); + return block_opt_encode(p_opt->more, p_opt->size, p_opt->number, p_encoded); +} + +uint32_t coap_block_opt_block2_decode(coap_block_opt_block2_t * p_opt, uint32_t encoded) +{ + NULL_PARAM_CHECK(p_opt); + return block_opt_decode(encoded, &p_opt->more, &p_opt->size, &p_opt->number); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_block.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_block.h new file mode 100644 index 0000000..fa2e9ee --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_block.h @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap_block.h + * + * @defgroup iot_sdk_coap_block CoAP Block transfer + * @ingroup iot_sdk_coap + * @{ + * @brief CoAP block transfer options encoding and decoding interface and definitions. + * + */ + +#ifndef COAP_BLOCK_H__ +#define COAP_BLOCK_H__ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define COAP_BLOCK_OPT_BLOCK_MORE_BIT_UNSET 0 /**< Value when more flag is set. */ +#define COAP_BLOCK_OPT_BLOCK_MORE_BIT_SET 1 /**< Value when more flag is not set. */ + +typedef struct +{ + uint8_t more; /**< More bit value. */ + uint16_t size; /**< Size of the block in bytes. */ + uint32_t number; /**< Block number. */ +} coap_block_opt_block1_t; + +typedef coap_block_opt_block1_t coap_block_opt_block2_t; + +/**@brief Encode block1 option into its uint binary counter part. + * + * @param[out] p_encoded Encoded version of the coap block1 option value. Must not be NULL. + * @param[in] p_opt Pointer to block1 option structure to be decoded into uint format. Must + * not be NULL. + * + * @retval NRF_SUCCESS If encoding of option was successful. + * @retval NRF_ERROR_NULL If one of the parameters supplied is a null pointer. + * @retval NRF_ERROR_INVALID_PARAM If one of the fields in the option structure has an illegal + * value. + */ +uint32_t coap_block_opt_block1_encode(uint32_t * p_encoded, coap_block_opt_block1_t * p_opt); + +/**@brief Decode block1 option from a uint to its structure counter part. + * + * @param[out] p_opt Pointer to block1 option structure to be filled by the function. Must not + * be NULL. + * @param[in] encoded Encoded version of the coap block1 option value. + * + * @retval NRF_SUCCESS If decoding of the option was successful. + * @retval NRF_ERROR_NULL If p_opt parameter is NULL. + * @retval NRF_ERROR_INVALID_PARAM If the block number is higher then allowed by spec (more than + 20 bits). + * @retval NRF_ERROR_INVALID_DATA If the size has the value of the reserved 2048 value (7). + */ +uint32_t coap_block_opt_block1_decode(coap_block_opt_block1_t * p_opt, uint32_t encoded); + +/**@brief Encode block2 option into its uint binary counter part. + * + * @param[out] p_encoded Encoded version of the coap block2 option value. Must not be NULL. + * @param[in] p_opt Pointer to block2 option structure to be decoded into uint format. Must + * not be NULL. + * + * @retval NRF_SUCCESS If encoding of option was successful. + * @retval NRF_ERROR_NULL If one of the parameters supplied is a null pointer. + * @retval NRF_ERROR_INVALID_PARAM If one of the fields in the option structure has an illegal + * value. + */ +uint32_t coap_block_opt_block2_encode(uint32_t * p_encoded, coap_block_opt_block2_t * p_opt); + +/**@brief Decode block2 option from a uint to its structure counter part. + * + * @param[out] p_opt Pointer to block2 option structure to be filled by the function. Must not + * be NULL. + * @param[in] encoded Encoded version of the coap block2 option value. + * + * @retval NRF_SUCCESS If decoding of the option was successful. + * @retval NRF_ERROR_NULL If p_opt parameter is NULL. + * @retval NRF_ERROR_INVALID_PARAM If the block number is higher then allowed by spec (more than + 20 bits). + * @retval NRF_ERROR_INVALID_DATA If the size has the value of the reserved 2048 value (7). + */ +uint32_t coap_block_opt_block2_decode(coap_block_opt_block2_t * p_opt, uint32_t encoded); + +#endif // COAP_BLOCK_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_codes.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_codes.h new file mode 100644 index 0000000..88b1d48 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_codes.h @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap_codes.h + * + * @defgroup iot_sdk_coap_codes CoAP Codes + * @ingroup iot_sdk_coap + * @{ + * @brief CoAP message and response codes. + */ + +#ifndef COAP_CODES_H__ +#define COAP_CODES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define COAP_CODE(c, dd) \ + ((((c) & 0x7) << 5) | (((1 ## dd) - 100) & 0x1F)) +/** + @note macro adds 1xx to the number in order to prevent dd from being + interpreted as octal. +*/ + +/*lint -save -e122 */ /* Suppress "Digit (8) too large for radix" */ + +/** @brief CoAP Message codes +*/ +typedef enum +{ + // CoAP Empty Message + COAP_CODE_EMPTY_MESSAGE = COAP_CODE(0,00), /**< CoAP code 0.00, Decimal: 0, Hex: 0x00. */ + + // CoAP Method Codes + COAP_CODE_GET = COAP_CODE(0,01), /**< CoAP code 0.01, Decimal: 1, Hex: 0x01. */ + COAP_CODE_POST = COAP_CODE(0,02), /**< CoAP code 0.02, Decimal: 2, Hex: 0x02. */ + COAP_CODE_PUT = COAP_CODE(0,03), /**< CoAP code 0.03, Decimal: 3, Hex: 0x03. */ + COAP_CODE_DELETE = COAP_CODE(0,04), /**< CoAP code 0.04, Decimal: 4, Hex: 0x04. */ + + // CoAP Success Response Codes + COAP_CODE_201_CREATED = COAP_CODE(2,01), /**< CoAP code 2.01, Decimal: 65, Hex: 0x41. */ + COAP_CODE_202_DELETED = COAP_CODE(2,02), /**< CoAP code 2.02, Decimal: 66, Hex: 0x42. */ + COAP_CODE_203_VALID = COAP_CODE(2,03), /**< CoAP code 2.03, Decimal: 67, Hex: 0x43. */ + COAP_CODE_204_CHANGED = COAP_CODE(2,04), /**< CoAP code 2.04, Decimal: 68, Hex: 0x44. */ + COAP_CODE_205_CONTENT = COAP_CODE(2,05), /**< CoAP code 2.05, Decimal: 69, Hex: 0x45. */ + COAP_CODE_231_CONTINUE = COAP_CODE(2,31), /**< CoAP code 2.31, Decimal: 95, Hex: 0x5F. */ + + // CoAP Client Error Response Codes + COAP_CODE_400_BAD_REQUEST = COAP_CODE(4,00), /**< CoAP code 4.00, Decimal: 128, Hex: 0x80. */ + COAP_CODE_401_UNAUTHORIZED = COAP_CODE(4,01), /**< CoAP code 4.01, Decimal: 129, Hex: 0x81. */ + COAP_CODE_402_BAD_OPTION = COAP_CODE(4,02), /**< CoAP code 4.02, Decimal: 130, Hex: 0x82. */ + COAP_CODE_403_FORBIDDEN = COAP_CODE(4,03), /**< CoAP code 4.03, Decimal: 131, Hex: 0x83. */ + COAP_CODE_404_NOT_FOUND = COAP_CODE(4,04), /**< CoAP code 4.04, Decimal: 132, Hex: 0x84. */ + COAP_CODE_405_METHOD_NOT_ALLOWED = COAP_CODE(4,05), /**< CoAP code 4.05, Decimal: 133, Hex: 0x85. */ + COAP_CODE_406_NOT_ACCEPTABLE = COAP_CODE(4,06), /**< CoAP code 4.06, Decimal: 134, Hex: 0x86. */ + COAP_CODE_408_REQUEST_ENTITY_INCOMPLETE = COAP_CODE(4,08), /**< CoAP code 4.08, Decimal: 136, Hex: 0x88. */ + COAP_CODE_412_PRECONDITION_FAILED = COAP_CODE(4,12), /**< CoAP code 4.12, Decimal: 140, Hex: 0x8C. */ + COAP_CODE_413_REQUEST_ENTITY_TOO_LARGE = COAP_CODE(4,13), /**< CoAP code 4.13, Decimal: 141, Hex: 0x8D. */ + COAP_CODE_415_UNSUPPORTED_CONTENT_FORMAT = COAP_CODE(4,15), /**< CoAP code 4.15, Decimal: 143, Hex: 0x8F. */ + + // CoAP Server Error Response Codes + COAP_CODE_500_INTERNAL_SERVER_ERROR = COAP_CODE(5,00), /**< CoAP code 5.00, Decimal: 160, Hex: 0xA0. */ + COAP_CODE_501_NOT_IMPLEMENTED = COAP_CODE(5,01), /**< CoAP code 5.01, Decimal: 161, Hex: 0xA1. */ + COAP_CODE_502_BAD_GATEWAY = COAP_CODE(5,02), /**< CoAP code 5.02, Decimal: 162, Hex: 0xA2. */ + COAP_CODE_503_SERVICE_UNAVAILABLE = COAP_CODE(5,03), /**< CoAP code 5.03, Decimal: 163, Hex: 0xA3. */ + COAP_CODE_504_GATEWAY_TIMEOUT = COAP_CODE(5,04), /**< CoAP code 5.04, Decimal: 164, Hex: 0xA4. */ + COAP_CODE_505_PROXYING_NOT_SUPPORTED = COAP_CODE(5,05) /**< CoAP code 5.05, Decimal: 165, Hex: 0xA5. */ +} coap_msg_code_t; + +/*lint -restore */ + +#ifdef __cplusplus +} +#endif + +#endif // COAP_CODES_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_message.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_message.c new file mode 100644 index 0000000..db0f7bf --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_message.c @@ -0,0 +1,804 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdlib.h> +#include <string.h> + +#include "nordic_common.h" +#include "coap_message.h" +#include "coap_api.h" +#include "iot_common.h" +#include "sdk_config.h" +#include "app_util.h" + +#define COAP_PAYLOAD_MARKER_SIZE 1 + +/**@brief Verify that there is a index available for a new option. */ +#define OPTION_INDEX_AVAIL_CHECK(COUNT) \ + if ((COUNT) >= COAP_MAX_NUMBER_OF_OPTIONS) \ + { \ + return (NRF_ERROR_NO_MEM | IOT_COAP_ERR_BASE); \ + } + +#if (COAP_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_COAP_ERR_BASE); \ + } +#else + +#define NULL_PARAM_CHECK(PARAM) +#define OPTION_INDEX_AVAIL_CHECK(COUNT) + +#endif // COAP_DISABLE_API_PARAM_CHECK + +uint32_t coap_message_create(coap_message_t * p_message, coap_message_conf_t * p_init_config) +{ + NULL_PARAM_CHECK(p_message); + NULL_PARAM_CHECK(p_init_config); + + // Setting default value for version. + p_message->header.version = COAP_VERSION; + + // Copy values from the init config. + p_message->header.type = p_init_config->type; + p_message->header.token_len = p_init_config->token_len; + p_message->header.code = p_init_config->code; + p_message->header.id = p_init_config->id; + p_message->response_callback = p_init_config->response_callback; + p_message->p_arg = NULL; + + if (p_init_config->port.port_number == 0) + { + return (NRF_ERROR_INVALID_PARAM | IOT_COAP_ERR_BASE); + } + memcpy(&p_message->port, &p_init_config->port, sizeof(coap_port_t)); + + memcpy(p_message->token, p_init_config->token, sizeof(p_init_config->token)); + return NRF_SUCCESS; +} + +/**@brief Decode CoAP option + * + * @param[in] p_raw_option Pointer to the memory buffer where the raw option is located. + * @param[inout] p_message Pointer to the current message. Used to retrieve information about + * where current option delta and the size of free memory to add the + * values of the option. Used as a container where to put + * the parsed option. + * @param[out] byte_count Number of bytes parsed. Used to indicate where the next option + * might be located (if any left) in the raw message buffer. + * + * @retval NRF_SUCCESS If the option parsing went successful. + * @retval NRF_ERROR_DATA_SIZE If there is no more space left in the free memory to add the + * option value to the p_message. + */ +static uint32_t decode_option(const uint8_t * p_raw_option, coap_message_t * p_message, uint16_t * byte_count) +{ + uint16_t byte_index = 0; + uint8_t option_num = p_message->options_count; + + // Calculate the option number. + uint16_t option_delta = (p_raw_option[byte_index] & 0xF0) >> 4; + // Calculate the option length. + uint16_t option_length = (p_raw_option[byte_index] & 0x0F); + byte_index++; + + uint16_t acc_option_delta = p_message->options_delta; + + if (option_delta == 13) + { + // read one additional byte to get the extended delta. + acc_option_delta += 13 + p_raw_option[byte_index++]; + + } + else if (option_delta == 14) + { + // read one additional byte to get the extended delta. + acc_option_delta += 269; + acc_option_delta += (p_raw_option[byte_index++] << 8); + acc_option_delta += (p_raw_option[byte_index++]); + } + else + { + acc_option_delta += option_delta; + } + + // Set the accumlated delta as the option number. + p_message->options[option_num].number = acc_option_delta; + + if (option_length == 13) + { + option_length = 13 + p_raw_option[byte_index++]; + } + else if (option_length == 14) + { + option_length = 269; + option_length += (p_raw_option[byte_index++] << 8); + option_length += p_raw_option[byte_index++]; + } + + // Set the option length including extended bytes. + p_message->options[option_num].length = option_length; + + // Point p_data to the memory where to find the option value. + p_message->options[option_num].p_data = (uint8_t *)&p_raw_option[byte_index]; + + // Update the delta counter with latest option number. + p_message->options_delta = p_message->options[option_num].number; + + byte_index += p_message->options[option_num].length; + *byte_count = byte_index; + + return NRF_SUCCESS; +} + + +/**@brief Encode CoAP option delta and length bytes. + * + * @param[inout] encoded_value Value to encode. In return the value after encoding. + * @param[out] encoded_value_ext The value of the encoded extended bytes. + * + * @return The size of the extended byte field. + */ +static inline uint8_t encode_extended_bytes(uint16_t * value, + uint16_t * value_ext) +{ + uint16_t raw_value = *value; + + uint8_t ext_size = 0; + + if (raw_value >= 269) + { + *value = 14; + *value_ext = raw_value - 269; + ext_size = 2; + } + else if (raw_value >= 13) + { + *value = 13; + *value_ext = raw_value - 13; + ext_size = 1; + } + else + { + *value = raw_value; + *value_ext = 0; + } + + return ext_size; +} + + +static uint32_t encode_option(uint8_t * p_buffer, coap_option_t * p_option, uint16_t * byte_count) +{ + uint16_t delta_ext = 0; + uint16_t delta = p_option->number; + + uint8_t delta_ext_size = encode_extended_bytes(&delta, + &delta_ext); + + uint16_t length = p_option->length; + uint16_t length_ext = 0; + + uint8_t length_ext_size = encode_extended_bytes(&length, + &length_ext); + + if (p_buffer == NULL) + { + uint16_t header_size = 1; + *byte_count = header_size + delta_ext_size + length_ext_size + p_option->length; + return NRF_SUCCESS; + } + + uint16_t byte_index = 0; + + // Add the option header. + p_buffer[byte_index++] = ((delta & 0x0F) << 4) | (length & 0x0F); + + // Add option delta extended bytes to the buffer. + if (delta_ext_size == 1) + { + // Add first byte of delta_ext to the option header. + p_buffer[byte_index++] = (uint8_t)delta_ext; + } + else if (delta_ext_size == 2) + { + // uint16 in Network Byte Order. + p_buffer[byte_index++] = (uint8_t)((delta_ext & 0xFF00) >> 8); + p_buffer[byte_index++] = (uint8_t)((delta_ext & 0x00FF)); + } + + if (length_ext_size == 1) + { + // Add first byte of length_ext to the option header. + p_buffer[byte_index++] = (uint8_t)length_ext; + } + else if (length_ext_size == 2) + { + // uint16 in Network Byte Order. + p_buffer[byte_index++] = (uint8_t)((length_ext & 0xFF00) >> 8); + p_buffer[byte_index++] = (uint8_t)((length_ext & 0x00FF)); + } + + memcpy(&p_buffer[byte_index], p_option->p_data, p_option->length); + *byte_count = byte_index + p_option->length; + + return NRF_SUCCESS; +} + +uint32_t coap_message_decode(coap_message_t * p_message, + const uint8_t * p_raw_message, + uint16_t message_len) +{ + NULL_PARAM_CHECK(p_message); + NULL_PARAM_CHECK(p_raw_message); + + // Check that the raw message contains the mandatory header. + if (message_len < 4) + { + return (NRF_ERROR_INVALID_LENGTH | IOT_COAP_ERR_BASE); + } + + // Parse the content of the raw message buffer. + uint16_t byte_index = 0; + + // Parse the 4 byte CoAP header. + p_message->header.version = (p_raw_message[byte_index] >> 6); + p_message->header.type = (coap_msg_type_t)((p_raw_message[byte_index] >> 4) & 0x03); + p_message->header.token_len = (p_raw_message[byte_index] & 0x0F); + byte_index++; + + p_message->header.code = (coap_msg_code_t)p_raw_message[byte_index]; + byte_index++; + + p_message->header.id = p_raw_message[byte_index++] << 8; + p_message->header.id += p_raw_message[byte_index++]; + + // Parse the token, if any. + for (uint8_t index = 0; (byte_index < message_len) && (index < p_message->header.token_len); index++) + { + p_message->token[index] = p_raw_message[byte_index++]; + } + + p_message->options_count = 0; + p_message->options_delta = 0; + + // Parse the options if any. + while ((byte_index < message_len) && (p_raw_message[byte_index] != COAP_PAYLOAD_MARKER)) + { + + uint32_t err_code; + uint16_t byte_count = 0; + + err_code = decode_option(&p_raw_message[byte_index], p_message, &byte_count); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + p_message->options_count += 1; + + byte_index += byte_count; + } + + // If there any more bytes to parse this would be the payload. + if (byte_index < message_len) + { + // Verify that we have a payload marker. + if (p_raw_message[byte_index] == COAP_PAYLOAD_MARKER) + { + byte_index++; + } + else + { + return COAP_MESSAGE_INVALID_CONTENT; + } + + p_message->payload_len = message_len - byte_index; + p_message->p_payload = (uint8_t *)&p_raw_message[byte_index]; + } + + return NRF_SUCCESS; +} + + +uint32_t coap_message_encode(coap_message_t * p_message, + uint8_t * p_buffer, + uint16_t * p_length) +{ + NULL_PARAM_CHECK(p_length); + NULL_PARAM_CHECK(p_message); + + // calculated size + uint16_t total_packet_size = 4; + + if (p_message->payload_len > 0) + { + total_packet_size += p_message->payload_len; + total_packet_size += COAP_PAYLOAD_MARKER_SIZE; + } + + if (p_message->header.token_len > 8) + { + return (NRF_ERROR_INVALID_DATA | IOT_COAP_ERR_BASE); + } + total_packet_size += p_message->header.token_len; + total_packet_size += p_message->options_len; + + // If this was a length check, return after setting the length in the output parameter. + if (*p_length == 0) + { + *p_length = total_packet_size; + return NRF_SUCCESS; + } + + // Check that the buffer provided is sufficient. + if (*p_length < total_packet_size) + { + return (NRF_ERROR_DATA_SIZE | IOT_COAP_ERR_BASE); + } + + if (((p_message->payload_len > 0 && p_message->p_payload == NULL)) || + (p_buffer == NULL)) + { + return COAP_MESSAGE_ERROR_NULL; + } + + // Start filling the bytes. + uint16_t byte_index = 0; + + // TODO: Verify the values of the header fields. + // if (version > 1) + // if (p_message->type > COAP_TYPE_RST) + // if (p_message->token_len > 8) + + + p_buffer[byte_index] = (((p_message->header.version & 0x3) << 6) | ((p_message->header.type & 0x3) << 4)) | (p_message->header.token_len & 0x0F); + byte_index++; + + p_buffer[byte_index] = p_message->header.code; + byte_index++; + + p_buffer[byte_index++] = (p_message->header.id & 0xFF00) >> 8; + p_buffer[byte_index++] = (p_message->header.id & 0x00FF); + + memcpy(&p_buffer[byte_index], p_message->token, p_message->header.token_len); + byte_index += p_message->header.token_len; + + //memcpy(&p_buffer[byte_index], &p_message->p_data[0], p_message->options_len); + for (uint8_t i = 0; i < p_message->options_count; i++) + { + uint32_t err_code; + + uint16_t byte_count = 0; + err_code = encode_option(&p_buffer[byte_index], &p_message->options[i], &byte_count); + if (err_code == NRF_SUCCESS) + { + byte_index += byte_count; + } + else + { + // Throw an error. + } + } + + if (p_message->payload_len > 0 && p_message->p_payload != NULL) + { + p_buffer[byte_index++] = 0xFF; + memcpy(&p_buffer[byte_index], p_message->p_payload, p_message->payload_len); + } + + *p_length = total_packet_size; + + return NRF_SUCCESS; +} + +uint32_t coap_message_opt_empty_add(coap_message_t * p_message, uint16_t option_num) +{ + OPTION_INDEX_AVAIL_CHECK(p_message->options_count); + + uint32_t err_code; + uint16_t encoded_len = 0; + uint8_t current_option_index = p_message->options_count; + + p_message->options[current_option_index].number = option_num - p_message->options_delta; + p_message->options[current_option_index].length = encoded_len; + + // Set accumulated option delta for next option. + p_message->options_delta = option_num; + + // Calculate option size + uint16_t option_byte_count = 0; + + // do a length check to encode_option to get the header length. + err_code = encode_option(NULL, &p_message->options[current_option_index], &option_byte_count); + + // Accumulate expected size of all options with headers. + p_message->options_len += option_byte_count; + + p_message->options_count += 1; + + return err_code; +} + +uint32_t coap_message_opt_uint_add(coap_message_t * p_message, + uint16_t option_num, + uint32_t data) +{ + OPTION_INDEX_AVAIL_CHECK(p_message->options_count); + + uint32_t err_code; + uint16_t encoded_len = p_message->data_len - p_message->options_offset; + uint8_t current_option_index = p_message->options_count; + uint8_t * p_next_option_data = &p_message->p_data[p_message->options_offset]; + + // If the value of the option is 0, do not encode the 0, as this can be omitted. (RFC7252 3.2) + if (data == 0) + { + encoded_len = 0; + } + else + { + err_code = coap_opt_uint_encode(p_next_option_data, &encoded_len, data); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + p_message->options[current_option_index].p_data = p_next_option_data; + p_message->options[current_option_index].number = option_num - p_message->options_delta; + p_message->options[current_option_index].length = encoded_len; + + // Set accumulated option delta for next option. + p_message->options_delta = option_num; + + // Calculate option size. + uint16_t option_byte_count = 0; + + // Do a length check to encode_option to get the header length. + err_code = encode_option(NULL, &p_message->options[current_option_index], &option_byte_count); + + // Accumulate expected size of all options with headers. + p_message->options_len += option_byte_count; + + p_message->options_count += 1; + + // Increase the pointer offset for the next option data in the scratch buffer. + p_message->options_offset += encoded_len; + + return err_code; +} + +uint32_t coap_message_opt_str_add(coap_message_t * p_message, uint16_t option_num, uint8_t * p_data, uint16_t length) +{ + OPTION_INDEX_AVAIL_CHECK(p_message->options_count); + + uint32_t err_code; + + uint16_t encoded_len = length; + uint8_t current_option_index = p_message->options_count; + uint8_t * p_next_option_data = &p_message->p_data[p_message->options_offset]; + + + err_code = coap_opt_string_encode(p_next_option_data, &encoded_len, p_data, length); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + p_message->options[current_option_index].p_data = p_next_option_data; + p_message->options[current_option_index].number = option_num - p_message->options_delta; + p_message->options[current_option_index].length = encoded_len; + + // Set accumulated option delta for next option. + p_message->options_delta = option_num; + + // Calculate option size + uint16_t option_byte_count = 0; + + // do a length check to encode_option to get the header length. + err_code = encode_option(NULL, &p_message->options[current_option_index], &option_byte_count); + + // Accumulate expected size of all options with headers. + p_message->options_len += option_byte_count; + + p_message->options_count += 1; + p_message->options_offset += encoded_len; + + return err_code; + +} + +uint32_t coap_message_opt_opaque_add(coap_message_t * p_message, uint16_t option_num, uint8_t * p_data, uint16_t length) +{ + OPTION_INDEX_AVAIL_CHECK(p_message->options_count); + + // Check if it is possible to add a new option of this length. + if ((p_message->data_len - p_message->options_offset) < length) + { + return (NRF_ERROR_DATA_SIZE | IOT_COAP_ERR_BASE); + } + + uint32_t err_code = NRF_SUCCESS; + + uint16_t encoded_len = length; + uint8_t current_option_index = p_message->options_count; + uint8_t * p_next_option_data = &p_message->p_data[p_message->options_offset]; + + + memcpy(p_next_option_data, p_data, encoded_len); + + p_message->options[current_option_index].p_data = p_next_option_data; + p_message->options[current_option_index].number = option_num - p_message->options_delta; + p_message->options[current_option_index].length = encoded_len; + + // Set accumulated option delta for next option. + p_message->options_delta = option_num; + + // Calculate option size + uint16_t option_byte_count = 0; + + // do a length check to encode_option to get the header length. + err_code = encode_option(NULL, &p_message->options[current_option_index], &option_byte_count); + + // Accumulate expected size of all options with headers. + p_message->options_len += option_byte_count; + + p_message->options_count += 1; + p_message->options_offset += encoded_len; + + return err_code; +} + +uint32_t coap_message_payload_set(coap_message_t * p_message, + void * p_payload, + uint16_t payload_len) +{ + // Check that there is available memory in the p_message->p_data scratch buffer. + if (payload_len > (COAP_MESSAGE_DATA_MAX_SIZE - p_message->options_offset)) + { + return (NRF_ERROR_NO_MEM | IOT_COAP_ERR_BASE); + } + + p_message->p_payload = &p_message->p_data[p_message->options_offset]; + p_message->payload_len = payload_len; + memcpy(p_message->p_payload, p_payload, payload_len); + + return NRF_SUCCESS; +} + + +uint32_t coap_message_remote_addr_set(coap_message_t * p_message, coap_remote_t * p_address) +{ + memcpy(&p_message->remote, p_address, sizeof(coap_remote_t)); + return NRF_SUCCESS; +} + +uint32_t coap_message_opt_index_get(uint8_t * p_index, coap_message_t * p_message, uint16_t option) +{ + NULL_PARAM_CHECK(p_index); + NULL_PARAM_CHECK(p_message); + + uint8_t index; + for (index = 0; index < p_message->options_count; index++) + { + if (p_message->options[index].number == option) + { + *p_index = index; + return NRF_SUCCESS; + } + } + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} + +uint32_t coap_message_opt_present(coap_message_t * p_message, uint16_t option) +{ + NULL_PARAM_CHECK(p_message); + + uint8_t index; + for (index = 0; index < p_message->options_count; index++) + { + if (p_message->options[index].number == option) + { + return NRF_SUCCESS; + } + } + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} + +static uint32_t bit_to_content_format(coap_content_type_t * p_ct, uint32_t bit) +{ + switch (bit) + { + case COAP_CT_MASK_PLAIN_TEXT: + *p_ct = COAP_CT_PLAIN_TEXT; + break; + + case COAP_CT_MASK_APP_LINK_FORMAT: + *p_ct = COAP_CT_APP_LINK_FORMAT; + break; + + case COAP_CT_MASK_APP_XML: + *p_ct = COAP_CT_APP_XML; + break; + + case COAP_CT_MASK_APP_OCTET_STREAM: + *p_ct = COAP_CT_APP_OCTET_STREAM; + break; + + case COAP_CT_MASK_APP_EXI: + *p_ct = COAP_CT_APP_EXI; + break; + + case COAP_CT_MASK_APP_JSON: + *p_ct = COAP_CT_APP_JSON; + break; + + default: + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); + } + return NRF_SUCCESS; +} + +static uint32_t content_format_to_bit(coap_content_type_t ct) +{ + uint32_t mask = 0; + switch (ct) + { + case COAP_CT_PLAIN_TEXT: + mask = COAP_CT_MASK_PLAIN_TEXT; + break; + + case COAP_CT_APP_LINK_FORMAT: + mask = COAP_CT_MASK_APP_LINK_FORMAT; + break; + + case COAP_CT_APP_XML: + mask = COAP_CT_MASK_APP_XML; + break; + + case COAP_CT_APP_OCTET_STREAM: + mask = COAP_CT_MASK_APP_OCTET_STREAM; + break; + + case COAP_CT_APP_EXI: + mask = COAP_CT_MASK_APP_EXI; + break; + + case COAP_CT_APP_JSON: + mask = COAP_CT_MASK_APP_JSON; + break; + + default: + break; + } + + return mask; +} + +uint32_t coap_message_ct_mask_get(coap_message_t * p_message, uint32_t * p_mask) +{ + NULL_PARAM_CHECK(p_message); + NULL_PARAM_CHECK(p_mask); + + (*p_mask) = 0; + + for (uint8_t index = 0; index < p_message->options_count; index++) + { + if (p_message->options[index].number == COAP_OPT_CONTENT_FORMAT) + { + uint32_t value; + uint32_t err_code = coap_opt_uint_decode(&value, + p_message->options[index].length, + p_message->options[index].p_data); + if (err_code == NRF_SUCCESS) + { + coap_content_type_t ct = (coap_content_type_t)value; + *p_mask |= content_format_to_bit(ct); + } + else + { + return err_code; + } + } + } + return NRF_SUCCESS; +} + +uint32_t coap_message_accept_mask_get(coap_message_t * p_message, uint32_t * p_mask) +{ + NULL_PARAM_CHECK(p_message); + NULL_PARAM_CHECK(p_mask); + + (*p_mask) = 0; + + for (uint8_t index = 0; index < p_message->options_count; index++) + { + if (p_message->options[index].number == COAP_OPT_ACCEPT) + { + uint32_t value; + uint32_t err_code = coap_opt_uint_decode(&value, + p_message->options[index].length, + p_message->options[index].p_data); + if (err_code == NRF_SUCCESS) + { + coap_content_type_t ct = (coap_content_type_t)value; + (*p_mask) |= content_format_to_bit(ct); + } + else + { + return err_code; + } + } + } + + return NRF_SUCCESS; +} + +uint32_t coap_message_ct_match_select(coap_content_type_t * p_ct, coap_message_t * p_message, coap_resource_t * p_resource) +{ + // Check ACCEPT options + uint32_t accept_mask = 0; + (void)coap_message_accept_mask_get(p_message, &accept_mask); + + if (accept_mask == 0) + { + // Default to plain text if option not set. + accept_mask = COAP_CT_MASK_PLAIN_TEXT; + } + + // Select the first common content-type between the resource and the CoAP client. + uint32_t common_ct = p_resource->ct_support_mask & accept_mask; + uint32_t bit_index; + for (bit_index = 0; bit_index < 32; bit_index++) + { + if (((common_ct >> bit_index) & 0x1 ) == 1) + { + break; + } + } + + uint32_t err_code = bit_to_content_format(p_ct, 1 << bit_index); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_message.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_message.h new file mode 100644 index 0000000..efd226b --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_message.h @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap_message.h + * + * @defgroup iot_sdk_coap_msg CoAP Message + * @ingroup iot_sdk_coap + * @{ + * @brief TODO. + */ + +#ifndef COAP_MESSAGE_H__ +#define COAP_MESSAGE_H__ + +#include <stdint.h> +#include "coap_api.h" +#include "coap_codes.h" +#include "coap_transport.h" +#include "coap_option.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define COAP_PAYLOAD_MARKER 0xFF + +/**@brief Create a new CoAP message. + * + * @details The function will allocate memory for the message internally and return + * a CoAP message structure. The callback provided will be called if a matching + * message ID/Token occurs in a response message. + * @param[out] p_message Pointer to set the generated coap_message_t structure to. + * @param[in] p_init_config Initial configuration parameters of the message to generate. + * + * @retval NRF_SUCCESS If memory for the new message was allocated and the + * initialization of the message went successfully. + * @retval NRF_ERROR_NULL Either the message or init_config parameter was NULL. + * @retval NRF_ERROR_INVALID_PARAM If port number in the port field is set to 0. + */ +uint32_t coap_message_create(coap_message_t * p_message, coap_message_conf_t * p_init_config); + +/**@brief Decode a message from a raw buffer. + * + * @details When the underlying transport layer receives a message, it has to + * be decoded into a CoAP message type structure. This functions returns + * a decoded message if decoding was successfully, or NULL otherwise. + * + * @param[out] p_message The generated coap_message_t after decoding the raw message. + * @param[in] p_raw_message Pointer to the encoded message memory buffer. + * @param[in] message_len Length of the p_raw_message. + * + * @retval NRF_SUCCESS If the decoding of the message succeeds. + * @retval NRF_ERROR_NULL If pointer to the p_message or p_raw_message were NULL. + * @retval NRF_ERROR_INVALID_LENGTH If the message is less than 4 bytes, not containing a + * full header. + * @retval COAP_MESSAGE_INVALID_CONTENT If the message could not be decoded successfully. This + * could happen if message length provided is larger than + * what is possible to decode (ex. missing payload marker). + * + */ +uint32_t coap_message_decode(coap_message_t * p_message, + const uint8_t * p_raw_message, + uint16_t message_len); + +/**@brief Encode a CoAP message into a byte buffer. + * + * @details This functions has two operations. One is the actual encoding into a + * byte buffer. The other is to query the size of a potential encoding. + * If p_buffer variable is omitted, the return value will be the size of a + * potential serialized message. This can be used to get some persistent memory from + * transport layer. The message have to be kept until all potential + * retransmissions has been attempted. + * + * The p_message can be deleted after this point if the function succeeds. + * + * @param[in] p_message Message to encode. + * @param[in] p_buffer Pointer to the byte buffer where to put the encoded message. + * @param[inout] p_length Length of the provided byte buffer passed in by reference. + * If the value 0 is supplied, the encoding will not take place, + * but only the dry run calculating the expected length of the + * encoded message. + * + * @retval NRF_SUCCESS If the encoding of the message succeeds. + * @retval NRF_ERROR_NULL If message or length parameter is NULL pointer. + * @retval NRF_ERROR_NO_MEM If the provided buffer is not sufficient for + * the encoded message. + * @retval COAP_MESSAGE_ERROR_NULL If the message has indicated the length of data, + * but memory pointer is NULL. + */ +uint32_t coap_message_encode(coap_message_t * p_message, + uint8_t * p_buffer, + uint16_t * p_length); + +/**@brief Get the content format mask of the message. + * + * @param[in] p_message Pointer to the message which to generate the content format mask from. + * Should not be NULL. + * @param[out] p_mask Value by reference to the variable to fill the result mask into. + * + * @retval NRF_SUCCESS If the mask could be generated. + * @retval NRF_ERROR_NULL If the message pointer or the mask pointer given was NULL. + */ +uint32_t coap_message_ct_mask_get(coap_message_t * p_message, uint32_t * p_mask); + +/**@brief Get the accept mask of the message. + * + * @param[in] p_message Pointer to the message which to generate the accept mask from. + * Should not be NULL. + * @param[out] p_mask Value by reference to the variable to fill the result mask into. + * + * @retval NRF_SUCCESS If the mask could be generated. + * @retval NRF_ERROR_NULL If the message pointer or the mask pointer given was NULL. + */ +uint32_t coap_message_accept_mask_get(coap_message_t * p_message, uint32_t * p_mask); + +#ifdef __cplusplus +} +#endif + +#endif // COAP_MESSAGE_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_observe.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_observe.c new file mode 100644 index 0000000..b4c60aa --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_observe.c @@ -0,0 +1,688 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <string.h> + +#include "coap_observe_api.h" +#include "coap_observe.h" +#include "nrf_error.h" +#include "iot_common.h" +#include "sdk_common.h" +#include "sdk_config.h" +#include "coap.h" + +#if IOT_COAP_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME coapobs + +#define NRF_LOG_LEVEL IOT_COAP_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR IOT_COAP_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR IOT_COAP_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define COAP_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define COAP_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define COAP_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define COAP_ENTRY() COAP_TRC(">> %s", __func__) +#define COAP_EXIT() COAP_TRC("<< %s", __func__) + +#else // IOT_COAP_CONFIG_LOG_ENABLED + +#define COAP_TRC(...) /**< Disables traces. */ +#define COAP_DUMP(...) /**< Disables dumping of octet streams. */ +#define COAP_ERR(...) /**< Disables error logs. */ + +#define COAP_ENTRY(...) +#define COAP_EXIT(...) + +#endif // IOT_COAP_CONFIG_LOG_ENABLED + +#if (COAP_ENABLE_OBSERVE_SERVER == 1) +static coap_observer_t m_observers[COAP_OBSERVE_MAX_NUM_OBSERVERS]; + +static void observe_server_init(void) +{ + COAP_ENTRY(); + + // Loop through the observer array and clear the memory. + for (uint32_t i = 0; i < COAP_OBSERVE_MAX_NUM_OBSERVERS; i++) + { + memset(&m_observers[i], 0, sizeof(coap_observer_t)); + } + + COAP_EXIT(); +} + +uint32_t internal_coap_observe_server_register(uint32_t * p_handle, coap_observer_t * p_observer) +{ + COAP_ENTRY(); + + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_observer); + + NULL_PARAM_MEMBER_CHECK(p_observer->p_resource_of_interest); + + // Check if there is already a registered observer in the list to be reused. + uint32_t handle; + uint32_t err_code = coap_observe_server_search(&handle, + &p_observer->remote, + p_observer->p_resource_of_interest); + if (err_code == NRF_SUCCESS) + { + memcpy(&m_observers[handle], p_observer, sizeof(coap_observer_t)); + *p_handle = handle; + return NRF_SUCCESS; + } + + // Check if there is an available spot in the observer list. + for (uint32_t i = 0; i < COAP_OBSERVE_MAX_NUM_OBSERVERS; i++) + { + if (m_observers[i].p_resource_of_interest == NULL) + { + memcpy(&m_observers[i], p_observer, sizeof(coap_observer_t)); + + *p_handle = i; + return NRF_SUCCESS; + } + } + + + COAP_EXIT(); + + return (NRF_ERROR_NO_MEM | IOT_COAP_ERR_BASE); +} + + +uint32_t internal_coap_observe_server_unregister(uint32_t handle) +{ + COAP_ENTRY(); + + if (handle >= COAP_OBSERVE_MAX_NUM_OBSERVERS) + { + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); + } + + if (m_observers[handle].p_resource_of_interest == NULL) + { + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); + } + + m_observers[handle].p_resource_of_interest = NULL; + + COAP_EXIT(); + + return NRF_SUCCESS; +} + + +uint32_t internal_coap_observe_server_search(uint32_t * p_handle, + coap_remote_t * p_observer_addr, + coap_resource_t * p_resource) +{ + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_observer_addr); + NULL_PARAM_CHECK(p_resource); + + for (uint32_t i = 0; i < COAP_OBSERVE_MAX_NUM_OBSERVERS; i++) + { + if (m_observers[i].p_resource_of_interest == p_resource) + { + if (m_observers[i].remote.port_number == p_observer_addr->port_number) + { + if (memcmp(p_observer_addr->addr, m_observers[i].remote.addr, sizeof(p_observer_addr->addr)) == 0) + { + *p_handle = i; + return NRF_SUCCESS; + } + } + } + } + + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} + + +uint32_t internal_coap_observe_server_next_get(coap_observer_t ** pp_observer, + coap_observer_t * p_observer, + coap_resource_t * p_resource) +{ + NULL_PARAM_CHECK(p_resource); + NULL_PARAM_CHECK(pp_observer); + + if (p_observer == NULL) + { + for (uint32_t i = 0; i < COAP_OBSERVE_MAX_NUM_OBSERVERS; i++) + { + if (m_observers[i].p_resource_of_interest == p_resource) + { + (*pp_observer) = &m_observers[i]; + return NRF_SUCCESS; + } + } + } + else + { + uint32_t index_to_previous = (uint8_t)(((uint32_t)p_observer - (uint32_t)m_observers) / (uint32_t)sizeof(coap_observer_t)); + + for (uint32_t i = index_to_previous + 1; i < COAP_OBSERVE_MAX_NUM_OBSERVERS; i++) + { + if (m_observers[i].p_resource_of_interest == p_resource) + { + (*pp_observer) = &m_observers[i]; + return NRF_SUCCESS; + } + } + } + (*pp_observer) = NULL; + + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} + +uint32_t internal_coap_observe_server_get(uint32_t handle, coap_observer_t ** pp_observer) +{ + NULL_PARAM_CHECK(pp_observer); + + if (handle >= COAP_OBSERVE_MAX_NUM_OBSERVERS) + { + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); + } + + if (m_observers[handle].p_resource_of_interest == NULL) + { + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); + } + + *pp_observer = &m_observers[handle]; + return NRF_SUCCESS; +} +#else +#define observe_server_init(...) +#endif + +#if (COAP_ENABLE_OBSERVE_CLIENT == 1) +static coap_observable_t m_observables[COAP_OBSERVE_MAX_NUM_OBSERVABLES]; + +static void observe_client_init(void) +{ + // Loop through the observable array and clear the memory. + for (uint32_t i = 0; i < COAP_OBSERVE_MAX_NUM_OBSERVABLES; i++) + { + memset(&m_observables[i], 0, sizeof(coap_observable_t)); + } +} + + +uint32_t internal_coap_observe_client_register(uint32_t * p_handle, + coap_observable_t * p_observable) +{ + COAP_ENTRY(); + + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_observable); + + NULL_PARAM_MEMBER_CHECK(p_observable->response_callback); + + // Check if there is an available spot in the observer list. + for (uint32_t i = 0; i < COAP_OBSERVE_MAX_NUM_OBSERVABLES; i++) + { + if (m_observables[i].response_callback == NULL) + { + memcpy(&m_observables[i], p_observable, sizeof(coap_observable_t)); + *p_handle = i; + return NRF_SUCCESS; + } + } + + COAP_EXIT(); + + return (NRF_ERROR_NO_MEM | IOT_COAP_ERR_BASE); +} + + +uint32_t internal_coap_observe_client_unregister(uint32_t handle) +{ + COAP_ENTRY(); + + if (handle >= COAP_OBSERVE_MAX_NUM_OBSERVABLES) + { + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); + } + + if (m_observables[handle].response_callback == NULL) + { + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); + } + + m_observables[handle].response_callback = NULL; + + COAP_EXIT(); + + return NRF_SUCCESS; +} + + +uint32_t internal_coap_observe_client_search(uint32_t * p_handle, uint8_t * p_token, uint16_t token_len) +{ + NULL_PARAM_CHECK(p_handle); + NULL_PARAM_CHECK(p_token); + + for (uint32_t i = 0; i < COAP_OBSERVE_MAX_NUM_OBSERVABLES; i++) + { + if ((m_observables[i].response_callback != NULL) && + (0 != m_observables[i].token_len) && + (memcmp(m_observables[i].token, p_token, m_observables[i].token_len) == 0)) + { + *p_handle = i; + return NRF_SUCCESS; + } + } + + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} + + +uint32_t internal_coap_observe_client_get(uint32_t handle, coap_observable_t ** pp_observable) +{ + NULL_PARAM_CHECK(pp_observable); + + if (handle >= COAP_OBSERVE_MAX_NUM_OBSERVABLES) + { + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); + } + + if (m_observables[handle].response_callback == NULL) + { + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); + } + + *pp_observable = &m_observables[handle]; + + return NRF_SUCCESS; +} + +uint32_t internal_coap_observe_client_next_get(coap_observable_t ** pp_observable, + uint32_t * p_handle, + coap_observable_t * p_observable) +{ + NULL_PARAM_CHECK(pp_observable); + + if (p_observable == NULL) + { + for (uint32_t i = 0; i < COAP_OBSERVE_MAX_NUM_OBSERVABLES; i++) + { + if (m_observables[i].response_callback != NULL) + { + (*pp_observable) = &m_observables[i]; + (*p_handle) = i; + return NRF_SUCCESS; + } + } + } + else + { + uint32_t index_to_previous = (uint8_t)(((uint32_t)p_observable - (uint32_t)m_observables) / (uint32_t)sizeof(coap_observable_t)); + + for (uint32_t i = index_to_previous + 1; i < COAP_OBSERVE_MAX_NUM_OBSERVABLES; i++) + { + if (m_observables[i].response_callback != NULL) + { + (*pp_observable) = &m_observables[i]; + (*p_handle) = i; + return NRF_SUCCESS; + } + } + } + (*pp_observable) = NULL; + + COAP_MUTEX_UNLOCK(); + + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} + +static uint32_t observe_opt_present(coap_message_t * p_message) +{ + uint8_t index; + for (index = 0; index < p_message->options_count; index++) + { + if (p_message->options[index].number == COAP_OPT_OBSERVE) + { + return NRF_SUCCESS; + } + } + return NRF_ERROR_NOT_FOUND; +} + +static void set_max_age(coap_observable_t * observable, coap_message_t * p_response) +{ + uint8_t index; + for (index = 0; index < p_response->options_count; index++) + { + if (p_response->options[index].number == COAP_OPT_MAX_AGE) + { + uint32_t max_age; + observable->max_age = coap_opt_uint_decode(&max_age, + p_response->options[index].length, + p_response->options[index].p_data); + observable->max_age = max_age; + return; + } + } + + // Max-Age option is not present, set default value to 60. + observable->max_age = 60; +} + +void coap_observe_client_send_handle(coap_message_t * p_request) +{ + COAP_ENTRY(); + + if (p_request->header.code == COAP_CODE_GET) + { + uint32_t observe_option = 0; + if (observe_opt_present(p_request) == NRF_SUCCESS) + { + // Locate option and check value. + uint8_t index; + for (index = 0; index < p_request->options_count; index++) + { + if (p_request->options[index].number == COAP_OPT_OBSERVE) + { + uint32_t err_code = coap_opt_uint_decode(&observe_option, + p_request->options[index].length, + p_request->options[index].p_data); + if (err_code != NRF_SUCCESS) + { + return; + } + break; + } + } + } + + if (observe_option == 1) + { + // Un-register observable instance. + uint32_t handle; + uint32_t err_code = internal_coap_observe_client_search(&handle, + p_request->token, + p_request->header.token_len); + if (err_code == NRF_SUCCESS) + { + (void)internal_coap_observe_client_unregister(handle); + COAP_TRC("OBSERVE=1 in request, client_unregister handle: %i", handle); + + } + } + } + + COAP_EXIT(); +} + +void coap_observe_client_response_handle(coap_message_t * p_response, coap_queue_item_t * p_item) +{ + COAP_ENTRY(); + + if (observe_opt_present(p_response) == NRF_SUCCESS) + { + if (p_item == NULL) + { + // Search for the token in the observable list. + uint32_t handle; + uint32_t err_code = internal_coap_observe_client_search(&handle, p_response->token, p_response->header.token_len); + if (err_code == NRF_SUCCESS) + { + // Fetch the observable. + coap_observable_t * p_observable; + err_code = internal_coap_observe_client_get(handle, &p_observable); + if (err_code == NRF_SUCCESS) + { + // Update max-age to the newly recieved message. + set_max_age(p_observable, p_response); + + COAP_MUTEX_UNLOCK(); + + // Callback to the application. + p_observable->response_callback(NRF_SUCCESS, NULL, p_response); + + COAP_MUTEX_LOCK(); + + COAP_TRC("Notification received on handle: %i", handle); + + #ifdef COAP_AUTOMODE + if (p_response->header.type == COAP_TYPE_CON) + { + // Reply an ACK upon CON message. + } + else if (p_response->header.type == COAP_TYPE_RST) + { + // Remove observable from list. + } + #endif + } + else + { + #ifdef COAP_AUTOMODE + if (p_response->header.type == COAP_TYPE_CON) + { + // Reply reset upon CON message when observer is not located. + } + #endif + } + } + else + { + // Send RST message back to server to indicate there is no one listening. + } + } + else // p_item set. + { + // If there is no observable instance created yet for thit token, add it. + uint32_t handle; + uint32_t err_code = internal_coap_observe_client_search(&handle, p_response->token, p_response->header.token_len); + if (err_code == (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE)) + { + // If the response is a valid response, add the observable resource. + if (p_response->header.code == COAP_CODE_205_CONTENT) + { + coap_observable_t observable; + // Token Length. + observable.token_len = p_response->header.token_len; + // Remote. + memcpy(&observable.remote, &p_response->remote, sizeof(coap_remote_t)); + // Token. + memcpy(observable.token, p_response->token, observable.token_len); + // Callback to be called upon notification. + observable.response_callback = p_item->callback; + + // Update max-age to the newly recieved message. + set_max_age(&observable, p_response); + + // Register the observable. + uint32_t observable_resource_handle; + (void)internal_coap_observe_client_register(&observable_resource_handle, &observable); + // TODO: error check + + COAP_TRC("Subscription response received, client_register handle: %i", observable_resource_handle); + } + } + } + } + else // COAP_OPT_OBSERVE not present + { + uint32_t handle; + uint32_t err_code = internal_coap_observe_client_search(&handle, p_response->token, p_response->header.token_len); + if (err_code == NRF_SUCCESS) + { + (void)internal_coap_observe_client_unregister(handle); + COAP_TRC("OBSERVE not present in notification, client_unregister handle: %i", handle); + } + } + + COAP_EXIT(); +} +#else +#define observe_client_init(...) +#endif + +void internal_coap_observe_init(void) +{ + observe_server_init(); + observe_client_init(); +} + +#if (COAP_ENABLE_OBSERVE_SERVER == 1) + +uint32_t coap_observe_server_register(uint32_t * p_handle, coap_observer_t * p_observer) +{ + COAP_MUTEX_UNLOCK(); + + uint32_t err_code = internal_coap_observe_server_register(p_handle, p_observer); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t coap_observe_server_unregister(uint32_t handle) +{ + COAP_MUTEX_UNLOCK(); + + uint32_t err_code = internal_coap_observe_server_unregister(handle); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t coap_observe_server_search(uint32_t * p_handle, coap_remote_t * p_observer_addr, coap_resource_t * p_resource) +{ + COAP_MUTEX_UNLOCK(); + + uint32_t err_code = internal_coap_observe_server_search(p_handle, p_observer_addr, p_resource); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t coap_observe_server_next_get(coap_observer_t ** pp_observer, coap_observer_t * p_observer, coap_resource_t * p_resource) +{ + COAP_MUTEX_UNLOCK(); + + uint32_t err_code = internal_coap_observe_server_next_get(pp_observer, p_observer, p_resource); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t coap_observe_server_get(uint32_t handle, coap_observer_t ** pp_observer) +{ + COAP_MUTEX_UNLOCK(); + + uint32_t err_code = internal_coap_observe_server_get(handle, pp_observer); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +#endif // COAP_ENABLE_OBSERVE_SERVER = 1 + +#if (COAP_ENABLE_OBSERVE_CLIENT == 1) + +uint32_t coap_observe_client_register(uint32_t * p_handle, coap_observable_t * p_observable) +{ + COAP_MUTEX_UNLOCK(); + + uint32_t err_code = internal_coap_observe_client_register(p_handle, p_observable); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t coap_observe_client_unregister(uint32_t handle) +{ + COAP_MUTEX_UNLOCK(); + + uint32_t err_code = internal_coap_observe_client_unregister(handle); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t coap_observe_client_search(uint32_t * p_handle, uint8_t * p_token, uint16_t token_len) +{ + COAP_MUTEX_UNLOCK(); + + uint32_t err_code = internal_coap_observe_client_search(p_handle, p_token, token_len); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t coap_observe_client_get(uint32_t handle, coap_observable_t ** pp_observable) +{ + COAP_MUTEX_UNLOCK(); + + uint32_t err_code = internal_coap_observe_client_get(handle, pp_observable); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t coap_observe_client_next_get(coap_observable_t ** pp_observable, uint32_t * p_handle, coap_observable_t * p_observable) +{ + COAP_MUTEX_UNLOCK(); + + uint32_t err_code = internal_coap_observe_client_next_get(pp_observable, p_handle, p_observable); + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + +#endif // COAP_ENABLE_OBSERVE_CLIENT == 1 diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_observe.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_observe.h new file mode 100644 index 0000000..bb72b89 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_observe.h @@ -0,0 +1,248 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap_observe.h + * + * @defgroup iot_sdk_coap_observe CoAP Observe + * @ingroup iot_sdk_coap + * @{ + * @brief Internal API of Nordic's CoAP Observe implementation. + */ +#ifndef COAP_OBSERVE_H__ +#define COAP_OBSERVE_H__ + +#include <stdint.h> + +#include "coap_observe_api.h" +#include "coap_api.h" +#include "coap_queue.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@cond NO_DOXYGEN */ + +/**@brief Register a new observer. + * + * @param[out] p_handle Handle to the observer instance registered. Returned by reference. + * Should not be NULL. + * @param[in] p_observer Pointer to the observer structure to register. The data will be + * copied. Should not be NULL. + * + * @retval NRF_SUCCESS If the observer was registered successfully. + * @retval NRF_ERROR_NO_MEM If the observer could not be added to the list. + * @retval NRF_ERROR_NULL If one of the parameters is a NULL pointer. + */ +uint32_t internal_coap_observe_server_register(uint32_t * p_handle, coap_observer_t * p_observer); + +/**@brief Unregister an observer. + * + * @details Unregister the observer and clear the memory used by this instance. + * + * @param[in] handle Handle to the observer instance registered. + * + * @retval NRF_SUCCESS If the observer was successfully unregistered. + * @retval NRF_ERROR_NOT_FOUND If the given handle was not found in the observer list. + */ +uint32_t internal_coap_observe_server_unregister(uint32_t handle); + +/**@brief Search the observer list for an observer matching remote address and subject given. + * + * @param[out] p_handle Handle to the observer instance registered. Returned by reference. + * Should not be NULL. + * @param[in] p_observer_addr Pointer to an address structure giving remote address of the observer and port number. + * Should not be NULL. + * @param[in] p_resource Pointer to the resource the observer is registered to. Should not be NULL. + * + * @retval NRF_SUCCESS If observer was found in the observer list. + * @retval NRF_ERROR_NULL If one of the pointers are NULL. + * @retval NRF_ERROR_NOT_FOUND If observer was not found. + */ +uint32_t internal_coap_observe_server_search(uint32_t * p_handle, coap_remote_t * p_observer_addr, coap_resource_t * p_resource); + +/**@brief Iterate through observers subscribing to a specific resource. + * + * @param[out] pp_observer Pointer to be filled by the search function upon finding the next observer starting from + * from the p_observer pointer provided. Should not be NULL. + * @param[in] p_observer Pointer to the observer where to start the search. + * @param[in] p_resource Pointer to the resource of interest. Should not be NULL. + * + * @retval NRF_SUCCESS If observer was found. + * @retval NRF_ERROR_NULL If pp_observer or p_resource pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If next observer was not found. + */ +uint32_t internal_coap_observe_server_next_get(coap_observer_t ** pp_observer, coap_observer_t * p_observer, coap_resource_t * p_resource); + +/**@brief Retrieve the observer based on handle. + * + * @param[in] handle Handle to the coap_observer_t instance. + * @param[out] pp_observer Pointer to an observer return by reference. + * Should not be NULL. + * + * @retval NRF_SUCCESS If observer was found in the observer list. + * @retval NRF_ERROR_NULL If pp_observer pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If observer associated with the handle was not found. + */ +uint32_t internal_coap_observe_server_get(uint32_t handle, coap_observer_t ** pp_observer); + +/**@brief Register a new observable resource. + * + * @param[out] p_handle Handle to the observable resource instance registered. Returned by + * reference. Should not be NULL. + * @param[in] p_observable Pointer to a observable resource structure to register. The structure + * will be copied. Should not be NULL. + * + * @retval NRF_SUCCESS If the observable resource was registered successfully. + * @retval NRF_ERROR_NO_MEM If the observable resource could not be added to the list. + * @retval NRF_ERROR_NULL If one of the parameters is a NULL pointer. + */ +uint32_t internal_coap_observe_client_register(uint32_t * p_handle, coap_observable_t * p_observable); + +/**@brief Unregister an observable resource. + * + * @details Unregister the observable resource and clear the memory used by this instance. + * + * @param[in] handle Handle to the observable resource instance registered. + * + * @retval NRF_SUCCESS If the observable resource was successfully unregistered. + * @retval NRF_ERROR_NOT_FOUND If the given handle was not found in the observable + * resource list. + */ +uint32_t internal_coap_observe_client_unregister(uint32_t handle); + +/**@brief Search for a observable resource instance by token. + * + * @param[out] p_handle Handle to the observable resource instance registered. Returned by + * reference. Should not be NULL. + * @param[in] p_token Pointer to the byte array holding the token id. Should not be NULL. + * @param[in] token_len Length of the token. + * + * @retval NRF_SUCCESS If observable resource was found in the observable + * resource list. + * @retval NRF_ERROR_NULL If one of the pointers are NULL. + * @retval NRF_ERROR_NOT_FOUND If observable resource was not found in the observable + * resource list. + */ +uint32_t internal_coap_observe_client_search(uint32_t * p_handle, uint8_t * p_token, uint16_t token_len); + +/**@brief Retrieve the observable resource based on handle. + * + * @param[in] handle Handle to the coap_observable_t instance. + * @param[out] pp_observable Pointer to an observable resource return by reference. + * Should not be NULL. + * + * @retval NRF_SUCCESS If observable resource was found in the observable + * resource list. + * @retval NRF_ERROR_NULL If pp_observable pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If observable resource associated with the handle + * was not found. + */ +uint32_t internal_coap_observe_client_get(uint32_t handle, coap_observable_t ** pp_observable); + +/**@brief Iterate through observable resources. + * + * @param[out] pp_observable Pointer to be filled by the search function upon finding the next + * observable resource starting from from the pointer provided. + * Should not be NULL. + * @param[out] p_handle Handler to the observable resource found returned by reference. Should + * not be NULL. + * @param[in] p_observable Pointer to the observable resource where to start the search. + * + * @retval NRF_SUCCESS If observer was found. + * @retval NRF_ERROR_NULL If pp_observer or p_observer pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If next observer was not found. + */ +uint32_t internal_coap_observe_client_next_get(coap_observable_t ** pp_observable, uint32_t * p_handle, coap_observable_t * p_observable); + + +#if (COAP_ENABLE_OBSERVE_SERVER == 1) || (COAP_ENABLE_OBSERVE_CLIENT == 1) + +/**@brief Internal function to initilize observer (client) and observable (server) lists. + * + * @retval NRF_SUCCESS If initialization was successful. + */ +void internal_coap_observe_init(void); + +#else // COAP_ENABLE_OBSERVE_SERVER || COAP_ENABLE_OBSERVE_CLIENT + +#define internal_coap_observe_init(...) + +#endif // COAP_ENABLE_OBSERVE_SERVER || COAP_ENABLE_OBSERVE_CLIENT + +#if (COAP_ENABLE_OBSERVE_CLIENT == 1) + +/**@brief Observe client function to be run when sending requests. + * + * @details The function will peek into the outgoing messages to see if any actions regarding + * subscription to observable resources has to be done. + * + * @param[in] p_request Pointer to the outgoing request. + */ +void coap_observe_client_send_handle(coap_message_t * p_request); + +/**@brief Observe client function to be run when response message has been received. + * + * @details The function will register and unregister observable resources based on the received + * response messages. Upon a notification max-age values will be updated, and the correct + * response callback will be called. If a notification is terminated by the peer, the function + * will automatically terminate the subscription from the client by unregistering the + * observable resource. + * + * @param[in] p_response Pointer to the response message received. + * @param[in] p_item Pointer to the queued element of the outgoing request. + */ +void coap_observe_client_response_handle(coap_message_t * p_response, coap_queue_item_t * p_item); + +#else // COAP_ENABLE_OBSERVE_CLIENT + +#define coap_observe_client_send_handle(...) +#define coap_observe_client_response_handle(...) + +#endif // COAP_ENABLE_OBSERVE_CLIENT + +/**@endcond */ + +#ifdef __cplusplus +} +#endif + +#endif // COAP_OBSERVE_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_observe_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_observe_api.h new file mode 100644 index 0000000..3075e88 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_observe_api.h @@ -0,0 +1,222 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap_observe_api.h + * + * @defgroup iot_sdk_coap_observe CoAP Observe + * @ingroup iot_sdk_coap + * @{ + * @brief Public API of Nordic's CoAP Observe implementation. + */ +#ifndef COAP_OBSERVE_API_H__ +#define COAP_OBSERVE_API_H__ + +#include <stdint.h> +#include "coap_api.h" +#include "coap_transport.h" +#include "coap_queue.h" +#include "compiler_abstraction.h" +#include "sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define COAP_OPT_OBSERVE 6 /**< Observe option number. */ + +/**@brief Struct for CoAP Server for holding an instance of a remote observer. */ +typedef struct +{ + coap_remote_t remote; /**< Remote address and port number. */ + uint8_t token[8]; /**< Message Token ID. */ + uint8_t token_len; /**< Length of the token. */ + coap_content_type_t ct; /**< Content type to use when sending notifications. */ + coap_resource_t * p_resource_of_interest; /**< Pointer to the resource of interest. */ +} coap_observer_t; + +/**@brief Struct for CoAP Client for holding an instance of a remote observable resource. */ +typedef struct +{ + coap_remote_t remote; /**< Remote address and port number. */ + uint8_t token[8]; /**< Message Token ID. */ + uint8_t token_len; /**< Length of the token. */ + coap_response_callback_t response_callback; /**< Function callback set by the application to be called when a notifications has been received. Should be set by the application. */ + uint32_t max_age; /**< Max-Age of the observable resources value. If timed out, the value is no longer valid as a representation of the observable resource. */ +} coap_observable_t; + + +/**@brief Register a new observer. + * + * @param[out] p_handle Handle to the observer instance registered. Returned by reference. + * Should not be NULL. + * @param[in] p_observer Pointer to the observer structure to register. The data will be + * copied. Should not be NULL. + * + * @retval NRF_SUCCESS If the observer was registered successfully. + * @retval NRF_ERROR_NO_MEM If the observer could not be added to the list. + * @retval NRF_ERROR_NULL If one of the parameters is a NULL pointer. + */ +uint32_t coap_observe_server_register(uint32_t * p_handle, coap_observer_t * p_observer); + +/**@brief Unregister an observer. + * + * @details Unregister the observer and clear the memory used by this instance. + * + * @param[in] handle Handle to the observer instance registered. + * + * @retval NRF_SUCCESS If the observer was successfully unregistered. + * @retval NRF_ERROR_NOT_FOUND If the given handle was not found in the observer list. + */ +uint32_t coap_observe_server_unregister(uint32_t handle); + +/**@brief Search the observer list for an observer matching remote address and subject given. + * + * @param[out] p_handle Handle to the observer instance registered. Returned by reference. + * Should not be NULL. + * @param[in] p_observer_addr Pointer to an address structure giving remote address of the observer and port number. + * Should not be NULL. + * @param[in] p_resource Pointer to the resource the observer is registered to. Should not be NULL. + * + * @retval NRF_SUCCESS If observer was found in the observer list. + * @retval NRF_ERROR_NULL If one of the pointers are NULL. + * @retval NRF_ERROR_NOT_FOUND If observer was not found. + */ +uint32_t coap_observe_server_search(uint32_t * p_handle, coap_remote_t * p_observer_addr, coap_resource_t * p_resource); + +/**@brief Iterate through observers subscribing to a specific resource. + * + * @param[out] pp_observer Pointer to be filled by the search function upon finding the next observer starting from + * from the p_observer pointer provided. Should not be NULL. + * @param[in] p_observer Pointer to the observer where to start the search. + * @param[in] p_resource Pointer to the resource of interest. Should not be NULL. + * + * @retval NRF_SUCCESS If observer was found. + * @retval NRF_ERROR_NULL If pp_observer or p_resource pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If next observer was not found. + */ +uint32_t coap_observe_server_next_get(coap_observer_t ** pp_observer, coap_observer_t * p_observer, coap_resource_t * p_resource); + +/**@brief Retrieve the observer based on handle. + * + * @param[in] handle Handle to the coap_observer_t instance. + * @param[out] pp_observer Pointer to an observer return by reference. + * Should not be NULL. + * + * @retval NRF_SUCCESS If observer was found in the observer list. + * @retval NRF_ERROR_NULL If pp_observer pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If observer associated with the handle was not found. + */ +uint32_t coap_observe_server_get(uint32_t handle, coap_observer_t ** pp_observer); + +/**@brief Register a new observable resource. + * + * @param[out] p_handle Handle to the observable resource instance registered. Returned by + * reference. Should not be NULL. + * @param[in] p_observable Pointer to a observable resource structure to register. The structure + * will be copied. Should not be NULL. + * + * @retval NRF_SUCCESS If the observable resource was registered successfully. + * @retval NRF_ERROR_NO_MEM If the observable resource could not be added to the list. + * @retval NRF_ERROR_NULL If one of the parameters is a NULL pointer. + */ +uint32_t coap_observe_client_register(uint32_t * p_handle, coap_observable_t * p_observable); + +/**@brief Unregister an observable resource. + * + * @details Unregister the observable resource and clear the memory used by this instance. + * + * @param[in] handle Handle to the observable resource instance registered. + * + * @retval NRF_SUCCESS If the observable resource was successfully unregistered. + * @retval NRF_ERROR_NOT_FOUND If the given handle was not found in the observable + * resource list. + */ +uint32_t coap_observe_client_unregister(uint32_t handle); + +/**@brief Search for a observable resource instance by token. + * + * @param[out] p_handle Handle to the observable resource instance registered. Returned by + * reference. Should not be NULL. + * @param[in] p_token Pointer to the byte array holding the token id. Should not be NULL. + * @param[in] token_len Length of the token. + * + * @retval NRF_SUCCESS If observable resource was found in the observable + * resource list. + * @retval NRF_ERROR_NULL If one of the pointers are NULL. + * @retval NRF_ERROR_NOT_FOUND If observable resource was not found in the observable + * resource list. + */ +uint32_t coap_observe_client_search(uint32_t * p_handle, uint8_t * p_token, uint16_t token_len); + +/**@brief Retrieve the observable resource based on handle. + * + * @param[in] handle Handle to the coap_observable_t instance. + * @param[out] pp_observable Pointer to an observable resource return by reference. + * Should not be NULL. + * + * @retval NRF_SUCCESS If observable resource was found in the observable + * resource list. + * @retval NRF_ERROR_NULL If pp_observable pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If observable resource associated with the handle + * was not found. + */ +uint32_t coap_observe_client_get(uint32_t handle, coap_observable_t ** pp_observable); + +/**@brief Iterate through observable resources. + * + * @param[out] pp_observable Pointer to be filled by the search function upon finding the next + * observable resource starting from from the pointer provided. + * Should not be NULL. + * @param[out] p_handle Handler to the observable resource found returned by reference. Should + * not be NULL. + * @param[in] p_observable Pointer to the observable resource where to start the search. + * + * @retval NRF_SUCCESS If observer was found. + * @retval NRF_ERROR_NULL If pp_observer or p_observer pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If next observer was not found. + */ +uint32_t coap_observe_client_next_get(coap_observable_t ** pp_observable, uint32_t * p_handle, coap_observable_t * p_observable); + +#ifdef __cplusplus +} +#endif + +#endif // COAP_OBSERVE_API_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_option.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_option.c new file mode 100644 index 0000000..0ae74be --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_option.c @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdlib.h> +#include <string.h> + +#include "coap_option.h" +#include "iot_common.h" + +#if (COAP_DISABLE_API_PARAM_CHECK == 0) + +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_COAP_ERR_BASE); \ + } + +#else + +#define NULL_PARAM_CHECK(PARAM) + +#endif // COAP_DISABLE_API_PARAM_CHECK + +uint32_t coap_opt_string_encode(uint8_t * p_encoded, uint16_t * p_length, uint8_t * p_string, uint16_t str_len) +{ + NULL_PARAM_CHECK(p_encoded); + NULL_PARAM_CHECK(p_length); + NULL_PARAM_CHECK(p_string); + + if (str_len > *p_length) + { + return (NRF_ERROR_DATA_SIZE | IOT_COAP_ERR_BASE); + } + + memcpy(p_encoded, p_string, str_len); + + *p_length = str_len; + + return NRF_SUCCESS; +} + +uint32_t coap_opt_string_decode(uint8_t * p_string, uint16_t * p_length, uint8_t * p_encoded) +{ + return NRF_SUCCESS; +} + +uint32_t coap_opt_uint_encode(uint8_t * p_encoded, uint16_t * p_length, uint32_t data) +{ + NULL_PARAM_CHECK(p_encoded); + NULL_PARAM_CHECK(p_length); + + uint16_t byte_index = 0; + + if (data <= UINT8_MAX) + { + if (*p_length < sizeof(uint8_t)) + { + return (NRF_ERROR_DATA_SIZE | IOT_COAP_ERR_BASE); + } + + p_encoded[byte_index++] = (uint8_t)data; + } + else if (data <= UINT16_MAX) + { + if (*p_length < sizeof(uint16_t)) + { + return (NRF_ERROR_DATA_SIZE | IOT_COAP_ERR_BASE); + } + + p_encoded[byte_index++] = (uint8_t)((data & 0xFF00) >> 8); + p_encoded[byte_index++] = (uint8_t)(data & 0x00FF); + } + else + { + if (*p_length < sizeof(uint32_t)) + { + return (NRF_ERROR_DATA_SIZE | IOT_COAP_ERR_BASE); + } + + p_encoded[byte_index++] = (uint8_t)((data & 0xFF000000) >> 24); + p_encoded[byte_index++] = (uint8_t)((data & 0x00FF0000) >> 16); + p_encoded[byte_index++] = (uint8_t)((data & 0x0000FF00) >> 8); + p_encoded[byte_index++] = (uint8_t)(data & 0x000000FF); + } + + + + *p_length = byte_index; + + return NRF_SUCCESS; +} + +uint32_t coap_opt_uint_decode(uint32_t * p_data, uint16_t length, uint8_t * p_encoded) +{ + NULL_PARAM_CHECK(p_data); + NULL_PARAM_CHECK(p_encoded); + + uint8_t byte_index = 0; + switch (length) + { + case 0: + { + *p_data = 0; + } + break; + + case 1: + { + *p_data = 0; + *p_data |= p_encoded[byte_index++]; + } + break; + + case 2: + { + *p_data = 0; + *p_data |= (p_encoded[byte_index++] << 8); + *p_data |= (p_encoded[byte_index++]); + } + break; + + case 3: + { + *p_data = 0; + *p_data |= (p_encoded[byte_index++] << 16); + *p_data |= (p_encoded[byte_index++] << 8); + *p_data |= (p_encoded[byte_index++]); + } + break; + + case 4: + { + *p_data = 0; + *p_data |= (p_encoded[byte_index++] << 24); + *p_data |= (p_encoded[byte_index++] << 16); + *p_data |= (p_encoded[byte_index++] << 8); + *p_data |= (p_encoded[byte_index++]); + } + break; + + default: + return (NRF_ERROR_INVALID_LENGTH | IOT_COAP_ERR_BASE); + } + + return NRF_SUCCESS; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_option.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_option.h new file mode 100644 index 0000000..3d5c5d9 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_option.h @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap_option.h + * + * @defgroup iot_sdk_coap_option CoAP Option + * @ingroup iot_sdk_coap + * @{ + * @brief Nordic's CoAP Option APIs. + */ + +#ifndef COAP_OPTION_H__ +#define COAP_OPTION_H__ + +/* + +-----+---+---+---+---+----------------+--------+--------+----------+ + | No. | C | U | N | R | Name | Format | Length | Default | + +-----+---+---+---+---+----------------+--------+--------+----------+ + | 1 | x | | | x | If-Match | opaque | 0-8 | (none) | + | 3 | x | x | - | | Uri-Host | string | 1-255 | (see | + | | | | | | | | | below) | + | 4 | | | | x | ETag | opaque | 1-8 | (none) | + | 5 | x | | | | If-None-Match | empty | 0 | (none) | + | 7 | x | x | - | | Uri-Port | uint | 0-2 | (see | + | | | | | | | | | below) | + | 8 | | | | x | Location-Path | string | 0-255 | (none) | + | 11 | x | x | - | x | Uri-Path | string | 0-255 | (none) | + | 12 | | | | | Content-Format | uint | 0-2 | (none) | + | 14 | | x | - | | Max-Age | uint | 0-4 | 60 | + | 15 | x | x | - | x | Uri-Query | string | 0-255 | (none) | + | 17 | x | | | | Accept | uint | 0-2 | (none) | + | 20 | | | | x | Location-Query | string | 0-255 | (none) | + | 23 | x | x | - | - | Block2 | uint | 0-3 | (none) | + | 27 | x | x | - | - | Block1 | uint | 0-3 | (none) | + | 28 | | | x | | Size2 | uint | 0-4 | (none) | + | 35 | x | x | - | | Proxy-Uri | string | 1-1034 | (none) | + | 39 | x | x | - | | Proxy-Scheme | string | 1-255 | (none) | + | 60 | | | x | | Size1 | uint | 0-4 | (none) | + +-----+---+---+---+---+----------------+--------+--------+----------+ +*/ + +#include <stdint.h> +#include "coap_api.h" +#include "nrf_error.h" +#include "sdk_errors.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum +{ + COAP_OPT_FORMAT_EMPTY = 0, + COAP_OPT_FORMAT_STRING = 1, + COAP_OPT_FORMAT_OPAQUE = 2, + COAP_OPT_FORMAT_UINT = 3 + +} coap_opt_format_t; + + +/**@brief Encode zero-terminated string into utf-8 encoded string. + * + * @param[out] p_encoded Pointer to buffer that will be used to fill the + * encoded string into. + * @param[inout] p_length Length of the buffer provided. Will also be used to + * return the size of the used buffer. + * @param[in] p_string String to encode. + * @param[in] str_len Length of the string to encode. + * + * @retval NRF_SUCCESS Indicates that encoding was successful. + * @retval NRF_ERROR_DATA_SIZE Indicates that the buffer provided was not sufficient to + * successfully encode the data. + */ +uint32_t coap_opt_string_encode(uint8_t * p_encoded, uint16_t * p_length, uint8_t * p_string, uint16_t str_len); + +/**@brief Decode a utf-8 string into a zero-terminated string. + * + * @param[out] p_string Pointer to the string buffer where the decoded + * string will be placed. + * @param[inout] p_length p_length of the encoded string. Returns the size of the buffer + * used in bytes. + * @param[in] p_encoded Buffer to decode. + * + * @retval NRF_SUCCESS Indicates that decoding was successful. + * @retval NRF_ERROR_DATA_SIZE Indicates that the buffer provided was not sufficient to + * successfully dencode the data. + */ +uint32_t coap_opt_string_decode(uint8_t * p_string, uint16_t * p_length, uint8_t * p_encoded); + +/**@brief Encode a uint value into a uint8_t buffer in network byte order. + * + * @param[out] p_encoded Pointer to buffer that will be used to fill the + * encoded uint into. + * @param[inout] p_length Length of the buffer provided. Will also be used to + * return the size of the used buffer. + * @param[in] data uint value which could be anything from 1 to 4 bytes. + * + * @retval NRF_SUCCESS Indicates that encoding was successful. + * @retval NRF_ERROR_DATA_SIZE Indicates that the buffer provided was not sufficient to + * successfully encode the data. + */ +uint32_t coap_opt_uint_encode(uint8_t * p_encoded, uint16_t * p_length, uint32_t data); + +/**@brief Decode a uint encoded value in network byte order to a uint32_t value. + * + * @param[out] p_data Pointer to the uint32_t value where the decoded uint will + * be placed. + * @param[inout] length Size of the encoded value. + * @param[in] p_encoded uint value to be decoded into a uint32_t value. + * + * @retval NRF_SUCCESS Indicates that decoding was successful. + * @retval NRF_ERROR_NULL If p_data or p_encoded pointer is NULL. + * @retval NRF_ERROR_INVALID_LENGTH If buffer was greater than uint32_t. + */ +uint32_t coap_opt_uint_decode(uint32_t * p_data, uint16_t length, uint8_t * p_encoded); + +#ifdef __cplusplus +} +#endif + +#endif // COAP_OPTION_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_queue.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_queue.c new file mode 100644 index 0000000..4e12bb6 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_queue.c @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <string.h> + +#include "coap_queue.h" +#include "iot_common.h" +#include "sdk_config.h" + +#if (COAP_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_COAP_ERR_BASE); \ + } +#else + +#define NULL_PARAM_CHECK(PARAM) + +#endif // COAP_DISABLE_API_PARAM_CHECK + +static coap_queue_item_t m_queue[COAP_MESSAGE_QUEUE_SIZE]; +static uint8_t m_message_queue_count = 0; + +uint32_t coap_queue_init(void) +{ + for (uint8_t i = 0; i < COAP_MESSAGE_QUEUE_SIZE; i++) + { + memset(&m_queue[i], 0, sizeof(coap_queue_item_t)); + m_queue[i].handle = i; + } + m_message_queue_count = 0; + + return NRF_SUCCESS; +} + +uint32_t coap_queue_add(coap_queue_item_t * item) +{ + NULL_PARAM_CHECK(item); + + if (m_message_queue_count >= COAP_MESSAGE_QUEUE_SIZE) + { + return (NRF_ERROR_NO_MEM | IOT_COAP_ERR_BASE); + } + else + { + for (uint8_t i = 0; i < COAP_MESSAGE_QUEUE_SIZE; i++) + { + if (m_queue[i].p_buffer == NULL) + { + // Free spot in message queue. Add message here... + memcpy(&m_queue[i], item, sizeof(coap_queue_item_t)); + + m_message_queue_count++; + + return NRF_SUCCESS; + } + } + + } + return (NRF_ERROR_DATA_SIZE | IOT_COAP_ERR_BASE); +} + +uint32_t coap_queue_remove(coap_queue_item_t * p_item) +{ + for (uint8_t i = 0; i < COAP_MESSAGE_QUEUE_SIZE; i++) + { + if (p_item == (coap_queue_item_t *)&m_queue[i]) + { + memset(&m_queue[i], 0, sizeof(coap_queue_item_t)); + m_message_queue_count--; + return NRF_SUCCESS; + } + } + + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} + +uint32_t coap_queue_item_by_token_get(coap_queue_item_t ** pp_item, uint8_t * p_token, uint8_t token_len) +{ + for (uint8_t i = 0; i < COAP_MESSAGE_QUEUE_SIZE; i++) + { + if (m_queue[i].token_len == token_len) + { + if ((0 != m_queue[i].token_len) && + (memcmp(m_queue[i].token, p_token, m_queue[i].token_len) == 0)) + { + *pp_item = &m_queue[i]; + return NRF_SUCCESS; + } + } + } + + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} + + +uint32_t coap_queue_item_by_mid_get(coap_queue_item_t ** pp_item, uint16_t message_id) +{ + + + for (uint8_t i = 0; i < COAP_MESSAGE_QUEUE_SIZE; i++) + { + if (m_queue[i].mid == message_id) + { + *pp_item = &m_queue[i]; + return NRF_SUCCESS; + } + } + + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} + + +uint32_t coap_queue_item_next_get(coap_queue_item_t ** pp_item, coap_queue_item_t * p_item) +{ + if (p_item == NULL) + { + for (uint8_t i = 0; i < COAP_MESSAGE_QUEUE_SIZE; i++) + { + if (m_queue[i].p_buffer != NULL) + { + (*pp_item) = &m_queue[i]; + return NRF_SUCCESS; + } + } + } + else + { + uint8_t index_to_previous = (uint8_t)(((uint32_t)p_item - (uint32_t)m_queue) / (uint32_t)sizeof(coap_queue_item_t)); + + for (uint8_t i = index_to_previous + 1; i < COAP_MESSAGE_QUEUE_SIZE; i++) + { + if (m_queue[i].p_buffer != NULL) + { + (*pp_item) = &m_queue[i]; + return NRF_SUCCESS; + } + } + } + (*pp_item) = NULL; + + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_queue.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_queue.h new file mode 100644 index 0000000..24c5db6 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_queue.h @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap_queue.h + * + * @defgroup iot_sdk_coap_queue CoAP Message Queue + * @ingroup iot_sdk_coap + * @{ + * @brief TODO. + */ + +#ifndef COAP_QUEUE_H__ +#define COAP_QUEUE_H__ + +#include <stdint.h> + +#include "coap_transport.h" +#include "coap_message.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + void * p_arg; /**< Miscellaneous pointer to application provided data that is associated with the message. Copied from the coap_message_t when creating the item. */ + uint32_t handle; /**< Quick reference to the handle value of the current item. */ + uint16_t mid; /**< Message ID. */ + uint8_t token_len; /**< Message Token length. */ + uint8_t token[8]; /**< Message Token value up to 8 bytes. */ + uint8_t retrans_count; /**< Re-transmission attempt count. */ + uint16_t timeout; /**< Time until new re-transmission attempt. */ + uint16_t timeout_val; /**< Last timeout value used. */ + coap_port_t port; /**< Source port to use when re-transmitting. */ + uint8_t * p_buffer; /**< Pointer to the data buffer containing the encoded CoAP message. */ + uint32_t buffer_len; /**< Size of the data buffer containing the encoded CoAP message. */ + coap_remote_t remote; /**< Destination address and port number to the remote. */ + coap_response_callback_t callback; /**< Callback function to be called upon response or transmission timeout. */ +} coap_queue_item_t; + +/**@brief Initilize the CoAP message queue. + * + * @retval NRF_SUCCESS If initialization completed successfully. + */ +uint32_t coap_queue_init(void); + +/**@brief Add item to the queue. + * + * @param[in] p_item Pointer to an item which to add to the queue. The function will copy all + * data provided. + * + * @retval NRF_SUCCESS If adding the item was successful. + * @retval NRF_ERROR_NO_MEM If max number of queued elements has been reached. This is + * configured by COAP_MESSAGE_QUEUE_SIZE in sdk_config.h. + * @retval NRF_ERROR_DATA_SIZE If the element could not be added. + */ +uint32_t coap_queue_add(coap_queue_item_t * p_item); + +/**@brief Remove item from the queue. + * + * @param[in] p_item Pointer to an item which to remove from the queue. Should not be NULL. + * + * @retval NRF_SUCCESS If the item was successfully removed from the queue. + * @retval NRF_ERROR_NULL If p_item pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If the item was not located in the queue. + */ +uint32_t coap_queue_remove(coap_queue_item_t * p_item); + +/**@brief Search for item by token. + * + * @details Search the items for any item matching the token. + * + * @param[out] pp_item Pointer to be filled by the function if item matching the token + * has been found. Should not be NULL. + * @param[in] p_token Pointer to token array to be matched. + * @param[in] token_len Length of the token to be matched. + * + * @retval NRF_SUCCESS If an item was successfully located. + * @retval NRF_ERROR_NULL If pp_item pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If no item was found. + */ +uint32_t coap_queue_item_by_token_get(coap_queue_item_t ** pp_item, uint8_t * p_token, uint8_t token_len); + +/**@brief Search for item by message id. + * + * @details Search the items for any item matching the message id. + * + * @param[out] pp_item Pointer to be filled by the funciton if item matching the message id + * has been found. Should not be NULL. + * @param[in] message_id Message id to be matched. + * + * @retval NRF_SUCCESS If an item was successfully located. + * @retval NRF_ERROR_NULL If pp_item pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If no item was found. + */ +uint32_t coap_queue_item_by_mid_get(coap_queue_item_t ** pp_item, uint16_t message_id); + +/**@brief Iterate through items. + * + * @param[out] pp_item Pointer to be filled by the search function upon finding the next + * queued item starting from the p_item pointer provided. Should + * not be NULL. + * @param[in] p_item Pointer to the item where to start the search. + * + * @retval NRF_SUCCESS If item was found. + * @retval NRF_ERROR_NULL If pp_item pointer is NULL. + * @retval NRF_ERROR_NOT_FOUND If next item was not found. + */ +uint32_t coap_queue_item_next_get(coap_queue_item_t ** pp_item, coap_queue_item_t * p_item); + +#ifdef __cplusplus +} +#endif + +#endif // COAP_QUEUE_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_resource.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_resource.c new file mode 100644 index 0000000..b928e0c --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_resource.c @@ -0,0 +1,274 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <string.h> + +#include "coap_resource.h" +#include "coap_api.h" +#include "iot_common.h" +#include "sdk_config.h" + +#define COAP_RESOURCE_MAX_AGE_INIFINITE 0xFFFFFFFF + +static coap_resource_t * mp_root_resource = NULL; +static char m_scratch_buffer[(COAP_RESOURCE_MAX_NAME_LEN + 1) * COAP_RESOURCE_MAX_DEPTH + 6]; + +#if (COAP_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_COAP_ERR_BASE); \ + } +#else + +#define NULL_PARAM_CHECK(PARAM) + +#endif // COAP_DISABLE_API_PARAM_CHECK + +uint32_t coap_resource_init(void) +{ + mp_root_resource = NULL; + return NRF_SUCCESS; +} + +uint32_t coap_resource_create(coap_resource_t * p_resource, const char * name) +{ + NULL_PARAM_CHECK(p_resource); + NULL_PARAM_CHECK(name); + + if (strlen(name) > COAP_RESOURCE_MAX_NAME_LEN) + { + return (NRF_ERROR_DATA_SIZE | IOT_COAP_ERR_BASE); + } + + memcpy(p_resource->name, name, strlen(name)); + + if (mp_root_resource == NULL) + { + mp_root_resource = p_resource; + } + + p_resource->max_age = COAP_RESOURCE_MAX_AGE_INIFINITE; + + return NRF_SUCCESS; +} + +uint32_t coap_resource_child_add(coap_resource_t * p_parent, coap_resource_t * p_child) +{ + NULL_PARAM_CHECK(p_parent); + NULL_PARAM_CHECK(p_child); + + if (p_parent->child_count == 0) + { + p_parent->p_front = p_child; + p_parent->p_tail = p_child; + } + else + { + coap_resource_t * p_last_sibling = p_parent->p_tail; + p_last_sibling->p_sibling = p_child; + p_parent->p_tail = p_child; + } + + p_parent->child_count++; + + return NRF_SUCCESS; +} + +static uint32_t generate_path(uint16_t buffer_pos, coap_resource_t * p_current_resource, char * parent_path, uint8_t * string, uint16_t * length) +{ + uint32_t err_code = NRF_SUCCESS; + + if (parent_path == NULL) + { + m_scratch_buffer[buffer_pos++] = '<'; + + if (p_current_resource->p_front != NULL) + { + coap_resource_t * next_child = p_current_resource->p_front; + do + { + err_code = generate_path(buffer_pos, next_child, m_scratch_buffer, string, length); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + next_child = next_child->p_sibling; + } while (next_child != NULL); + } + } + else + { + uint16_t size = strlen(p_current_resource->name); + m_scratch_buffer[buffer_pos++] = '/'; + + memcpy(&m_scratch_buffer[buffer_pos], p_current_resource->name, size); + buffer_pos += size; + + if (p_current_resource->p_front != NULL) + { + coap_resource_t * next_child = p_current_resource->p_front; + do + { + err_code = generate_path(buffer_pos, next_child, m_scratch_buffer, string, length); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + next_child = next_child->p_sibling; + } while (next_child != NULL); + } + + m_scratch_buffer[buffer_pos++] = '>'; + + // If the resource is observable, append 'obs;' token. + if ((p_current_resource->permission & COAP_PERM_OBSERVE) > 0) + { + memcpy(&m_scratch_buffer[buffer_pos], ";obs", 4); + buffer_pos += 4; + } + + m_scratch_buffer[buffer_pos++] = ','; + + if (buffer_pos <= (*length)) + { + *length -= buffer_pos; + memcpy(&string[strlen((char *)string)], m_scratch_buffer, buffer_pos); + } + else + { + return (NRF_ERROR_DATA_SIZE | IOT_COAP_ERR_BASE); + } + } + + return err_code; +} + +uint32_t coap_resource_well_known_generate(uint8_t * string, uint16_t * length) +{ + NULL_PARAM_CHECK(string); + NULL_PARAM_CHECK(length); + + if (mp_root_resource == NULL) + { + return (NRF_ERROR_INVALID_STATE | IOT_COAP_ERR_BASE); + } + + memset(string, 0, *length); + + uint32_t err_code = generate_path(0, mp_root_resource, NULL, string, length); + + string[strlen((char *)string) - 1] = '\0'; // remove the last comma + + return err_code; +} + +static coap_resource_t * coap_resource_child_resolve(coap_resource_t * p_parent, + char * p_path) +{ + coap_resource_t * result = NULL; + if (p_parent->p_front != NULL) + { + coap_resource_t * sibling_in_question = p_parent->p_front; + + do { + // Check if the sibling name match. + size_t size = strlen(sibling_in_question->name); + if (strncmp(sibling_in_question->name, p_path, size) == 0) + { + return sibling_in_question; + } + else + { + sibling_in_question = sibling_in_question->p_sibling; + } + } while (sibling_in_question != NULL); + } + return result; +} + +uint32_t coap_resource_get(coap_resource_t ** p_resource, uint8_t ** pp_uri_pointers, uint8_t num_of_uris) +{ + if (mp_root_resource == NULL) + { + // Make sure pointer is set to NULL before returning. + *p_resource = NULL; + return (NRF_ERROR_INVALID_STATE | IOT_COAP_ERR_BASE); + } + + coap_resource_t * p_current_resource = mp_root_resource; + + // Every node should start at root. + for (uint8_t i = 0; i < num_of_uris; i++) + { + p_current_resource = coap_resource_child_resolve(p_current_resource, (char *)pp_uri_pointers[i]); + + if (p_current_resource == NULL) + { + // Stop looping as this direction will not give anything more. + break; + } + } + + if (p_current_resource != NULL) + { + *p_resource = p_current_resource; + return NRF_SUCCESS; + } + + // If nothing has been found. + *p_resource = NULL; + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); +} + +uint32_t coap_resource_root_get(coap_resource_t ** pp_resource) +{ + NULL_PARAM_CHECK(pp_resource); + + if (mp_root_resource == NULL) + { + return (NRF_ERROR_NOT_FOUND | IOT_COAP_ERR_BASE); + } + + *pp_resource = mp_root_resource; + + return NRF_SUCCESS; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_resource.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_resource.h new file mode 100644 index 0000000..d73ef1f --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_resource.h @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap_resource.h + * + * @defgroup iot_sdk_coap_resource CoAP Resource + * @ingroup iot_sdk_coap + * @{ + * @brief Private API of Nordic's CoAP Resource implementation. + */ + +#ifndef COAP_RESOURCE_H__ +#define COAP_RESOURCE_H__ + +#include <stdint.h> +#include "coap_api.h" +#include "sdk_config.h" +#include "coap_message.h" +#include "nrf_error.h" +#include "sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Initialize the CoAP resource module. + * + * @details This function will initialize the root element pointer to NULL. + * This way, a new root can be assigned registered. The first + * resource added will be set as the new root. + * + * @retval NRF_SUCCESS This function will always return success. + */ +uint32_t coap_resource_init(void); + +/**@brief Find a resource by traversing the resource names. + * + * @param[out] p_resource Located resource. + * @param[in] pp_uri_pointers Array of strings which forms the hierarchical path to the resource. + * @param[in] num_of_uris Number of URIs supplied through the path pointer list. + * + * @retval NRF_SUCCESS The resource was instance located. + * @retval NRF_ERROR_NOT_FOUND The resource was not located. + * @retval NRF_ERROR_INVALID_STATE If no resource has been registered. + */ +uint32_t coap_resource_get(coap_resource_t ** p_resource, + uint8_t ** pp_uri_pointers, + uint8_t num_of_uris); + + +/**@brief Process the request related to the resource. + * + * @details When a request is received and the resource has successfully been located it + * will pass on to this function. The method in the request will be matched against + * what the service provides of method handling callbacks. If the request expects a + * response this will be provided as output from this function. The memory provided + * for the response must be provided from outside. + * + * @param[in] p_resource Resource that will handle the request. + * @param[in] p_request The request to be handled. + * @param[inout] p_response Response message which can be used by the resource populate + * the response message. + */ +uint32_t coap_resource_process_request(coap_resource_t * p_resource, + coap_message_t * p_request, + coap_message_t * p_response); + +#ifdef __cplusplus +} +#endif + +#endif // COAP_MESSAGE_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport.h new file mode 100644 index 0000000..b9fd597 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport.h @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file coap_transport.h + * + * @defgroup iot_sdk_coap_transport CoAP transport abstraction + * @ingroup iot_sdk_coap + * @{ + * @brief The transport interface that the CoAP depends on for sending and receiving CoAP messages. + * + * @details While the interface is well defined and should not be altered, the implementation of the + * interface depends on the choice of IP stack. The only exception to this is the + * \ref coap_transport_read API. This API is implemented in the CoAP, and the transport layer is + * expected to call this function when data is received on one of the CoAP ports. + */ + +#ifndef COAP_TRANSPORT_H__ +#define COAP_TRANSPORT_H__ + +#include <stdint.h> +#include <nrf_tls.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Port identification information. */ +typedef struct +{ + uint16_t port_number; /**< Port number. */ +} coap_port_t; + + +/**@brief Remote endpoint. */ +typedef struct +{ + uint8_t addr[16]; /**< Address of the remote device. */ + uint16_t port_number; /**< Remote port number. */ +} coap_remote_t; + + +/**@brief Transport initialization information. */ +typedef struct +{ + coap_port_t * p_port_table; /**< Information about the ports being registered. Count is assumed to be COAP_PORT_COUNT. */ + void * p_arg; /**< Public. Miscellaneous pointer to application provided data that should be passed to the transport. */ +} coap_transport_init_t; + + + +/**@brief Initializes the transport layer to have the data ports set up for CoAP. + * + * @param[in] p_param Port count and port numbers. + * + * @retval NRF_SUCCESS If initialization was successful. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t coap_transport_init (const coap_transport_init_t * p_param); + + +/**@brief Sends data on a CoAP endpoint or port. + * + * @param[in] p_port Port on which the data is to be sent. + * @param[in] p_remote Remote endpoint to which the data is targeted. + * @param[in] p_data Pointer to the data to be sent. + * @param[in] datalen Length of the data to be sent. + * + * @retval NRF_SUCCESS If the data was sent successfully. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t coap_transport_write(const coap_port_t * p_port, + const coap_remote_t * p_remote, + const uint8_t * p_data, + uint16_t datalen); + + + +/**@brief Handles data received on a CoAP endpoint or port. + * + * This API is not implemented by the transport layer, but assumed to exist. This approach + * avoids unnecessary registering of callback and remembering it in the transport layer. + * + * @param[in] p_port Port on which the data is received. + * @param[in] p_remote Remote endpoint from which the data is received. + * @param[in] p_local Local endpoint on which the data is received. + * @param[in] result Indicates if the data was processed successfully by lower layers. + * Possible failures could be NRF_SUCCESS, + * UDP_BAD_CHECKSUM, + * UDP_TRUNCATED_PACKET, or + * UDP_MALFORMED_PACKET. + * @param[in] p_data Pointer to the data received. + * @param[in] datalen Length of the data received. + * + * @retval NRF_SUCCESS If the data was handled successfully. Otherwise, an error code that indicates the reason for the failure is returned. + * + */ +uint32_t coap_transport_read(const coap_port_t * p_port, + const coap_remote_t * p_remote, + const coap_remote_t * p_local, + uint32_t result, + const uint8_t * p_data, + uint16_t datalen); + + +/**@brief Process loop to handle DTLS processing. + * + * @details The function handles any processing of encrypted packets. + * Some encryption libraries requires to be run in a processing + * loop. This function is called by the CoAP library everytime + * \ref coap_time_tick is issued from the library user. Any other process + * specific routines that should be done regularly could be added in + * this function. + */ +void coap_transport_process(void); + +/**@brief Process loop when using coap BSD socket transport implementation. + * + * @details This is blocking call. The function unblock is only + * triggered upon an socket event registered to select() by coap transport. + * This function must be called as often as possible in order to dispatch incomming + * socket events. Preferred to be put in the application's main loop or similar. + */ +void coap_transport_input(void); + +#ifdef __cplusplus +} +#endif + +#endif //COAP_TRANSPORT_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_dtls.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_dtls.c new file mode 100644 index 0000000..b1fe138 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_dtls.c @@ -0,0 +1,996 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "nrf_error.h" +#include "nrf_drv_rng.h" +#include "sdk_config.h" +#include "iot_common.h" +#include "iot_pbuffer.h" +#include "coap_transport.h" +#include "coap.h" +#include "udp_api.h" +#include "nrf_tls.h" +#include "mem_manager.h" +#include "iot_errors.h" + + +#if IOT_COAP_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME coap_dtls + +#define NRF_LOG_LEVEL IOT_COAP_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR IOT_COAP_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR IOT_COAP_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define COAPT_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define COAPT_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define COAPT_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define COAPT_ENTRY() COAPT_TRC(">> %s", __func__) +#define COAPT_EXIT() COAPT_TRC("<< %s", __func__) +#define COAPT_ENTRY_WITH_READLEN(read_len) COAPT_TRC(">> %s, readlen %d", __func__, read_len) +#define COAPT_EXIT_WITH_RESULT(result) COAPT_TRC("<< %s, result 0x%08lX", __func__, result) + +#else // IOT_COAP_CONFIG_LOG_ENABLED + +#define COAPT_TRC(...) /**< Disables traces. */ +#define COAPT_DUMP(...) /**< Disables dumping of octet streams. */ +#define COAPT_ERR(...) /**< Disables error logs. */ + +#define COAPT_ENTRY(...) +#define COAPT_EXIT(...) +#define COAPT_ENTRY_WITH_READLEN(...) +#define COAPT_EXIT_WITH_RESULT(...) + +#endif // IOT_COAP_CONFIG_LOG_ENABLED + +/**@brief Max size to be requested from the DTLS library when polling for decoded CoAP data. */ +#define MAX_BUFFER_SIZE 1024 + + +/**@brief UDP port information. */ +typedef struct +{ + uint32_t socket_id; /**< Socket information provided by UDP. */ + uint16_t port_number; /**< Associated port number. */ + nrf_tls_key_settings_t * p_settings; /**< Key preferences. */ +} udp_port_t; + +/**@brief CoAP remote session. Encapsulates information needed for DTLS. */ +typedef struct +{ + nrf_tls_instance_t dtls_instance; /**< DTLS instance identifier. */ + coap_remote_t remote_endpoint; /**< Remote endoint indentification. */ + uint16_t local_port_index; /**< Identifies local endpoint assoicated with the session. */ +} coap_remote_session_t; + +/**@brief Possible CoAP transport types. Needed for internal handling of events and data. */ +typedef enum +{ + COAP_TRANSPORT_NON_SECURE_DATAGRAM = 0, /**< Non-secure transport, no DTLS procedures apply. */ + COAP_TRANSPORT_SECURE_DATAGRAM = 1, /**< Secure transport, DTLS procedures apply. */ + COAP_TRANSPORT_MAX_TYPES = 2 /**< Maximum transport types. Not a valid transport identifer used as max count. */ +} coap_transport_type_t; + +/** + * @brief Transport write handler signature. + * + * @param[in] p_remote Remote endpoint on which data is to be written. + * @param[in] local_port_index Local endpoint identifier writing the data. + * @param[in] p_data Data to be written. + * @param[in] datalen Length of data to be written. + * + * @retval NRF_SUCCESS if the procedure was successful, else an error code indicating reason for + * failure. + */ +typedef uint32_t (* port_write_t) (const coap_remote_t * const p_remote, + uint32_t local_port_index, + const uint8_t * p_data, + uint16_t datalen); + +/** + * @brief Transport read handler signature. + * + * @param[in] p_remote Remote endpoint which sent data. + * @param[in] local_port_index Local endpoint identifier to which the data was sent. + * @param[in] p_data Data read on the transport. + * @param[in] datalen Length of data read on the transport. + * + * @retval NRF_SUCCESS if the procedure was successful, else an error code indicating reason for + * failure. + */ +typedef uint32_t (* port_read_t) (const coap_remote_t * const p_remote, + uint32_t local_port_index, + uint32_t read_result, + const uint8_t * p_data, + uint16_t datalen); + +/** Forward declaration. */ +uint32_t non_secure_datagram_write (const coap_remote_t * const p_remote, + uint32_t local_port_index, + const uint8_t * p_data, + uint16_t datalen); + +/** Forward declaration. */ +uint32_t secure_datagram_write (const coap_remote_t * const p_remote, + uint32_t local_port_index, + const uint8_t * p_data, + uint16_t datalen); + +/** Forward declaration. */ +uint32_t non_secure_datagram_read (const coap_remote_t * const p_remote, + uint32_t local_port_index, + uint32_t read_result, + const uint8_t * p_data, + uint16_t datalen); + +/** Forward declaration. */ +uint32_t secure_datagram_read(const coap_remote_t * const p_remote, + uint32_t local_port_index, + uint32_t read_result, + const uint8_t * p_data, + uint16_t datalen); + +/** Forward declaration. */ +uint32_t dtls_output_handler(nrf_tls_instance_t const * p_instance, + uint8_t const * p_data, + uint32_t datalen); + +static udp_port_t m_port_table[COAP_PORT_COUNT]; /**< Table maintaining association between CoAP ports and corresponding UDP socket identifiers. */ +static coap_remote_session_t m_remote_session[COAP_DTLS_MAX_REMOTE_SESSION]; /**< Table for managing security sessions with remote endpoints. */ + +/**@brief Table of transport write handlers. */ +const port_write_t port_write_fn[COAP_TRANSPORT_MAX_TYPES] = +{ + non_secure_datagram_write, + secure_datagram_write +}; + +/**@brief Table of transport read handlers. */ +const port_read_t port_read_fn[COAP_TRANSPORT_MAX_TYPES] = +{ + non_secure_datagram_read, + secure_datagram_read +}; + + +/** + * @brief Searches the local port reference based on the port number. + * + * @param[out] p_index Pointer where local port refernce should be provided (if found). + * @param[in] port_query Port number for which local port reference is requested. + * + * @retval NRF_SUCCESS if procedure succeeded else NRF_ERROR_NOT_FOUND. + */ +static uint32_t local_port_index_get(uint32_t * p_index, uint16_t port_query) +{ + uint32_t local_port_index; + + // Find local port index. + for (local_port_index = 0; local_port_index < COAP_PORT_COUNT; local_port_index++) + { + if (m_port_table[local_port_index].port_number == port_query) + { + break; + } + } + + // If we could not find the local port requested in the port table. + if (local_port_index >= COAP_PORT_COUNT) + { + return NRF_ERROR_NOT_FOUND; + } + + *p_index = local_port_index; + + return NRF_SUCCESS; +} + + +/** + * @brief Session to be initialized/freed. + * + * @param[in] p_session Session + */ +static void remote_session_init(coap_remote_session_t * p_session) +{ + memset(p_session, 0, sizeof(coap_remote_session_t)); + NRF_TLS_INTSANCE_INIT(&p_session->dtls_instance); +} + + +/** + * @brief Creates DTLS session between remote and local endpoint. + * + * @param[in] local_port_index Identifies local endpoint. + * @param[in] role Identifies DTLS role to be played (server or client). + * @param[in] p_remote Identifies remote endpoint. + * @param[in] p_settings Security settings to be used for the session. + * @param[out] pp_session Pointer to the session created (if any). + * + * @retval NRF_SUCCESS is procedure succeeded else an error code indicating reason for failure. + */ +static uint32_t session_create(uint32_t local_port_index, + nrf_tls_role_t role, + const coap_remote_t * const p_remote, + nrf_tls_key_settings_t * const p_settings, + coap_remote_session_t ** pp_session) +{ + uint32_t err_code = NRF_ERROR_NO_MEM; + + for (uint32_t index = 0; index < COAP_DTLS_MAX_REMOTE_SESSION; index++) + { + if (m_remote_session[index].remote_endpoint.port_number == 0) + { + // Found free session. + m_remote_session[index].remote_endpoint.port_number = p_remote->port_number; + memcpy(m_remote_session[index].remote_endpoint.addr, p_remote->addr, IPV6_ADDR_SIZE); + m_remote_session[index].local_port_index = local_port_index; + + // Attempt Allocate TLS session. + const nrf_tls_options_t dtls_options = + { + .output_fn = dtls_output_handler, + .transport_type = NRF_TLS_TYPE_DATAGRAM, + .role = role, + .p_key_settings = p_settings + }; + + m_remote_session[index].dtls_instance.transport_id = index; + + COAP_MUTEX_UNLOCK(); + + err_code = nrf_tls_alloc(&m_remote_session[index].dtls_instance, &dtls_options); + + COAP_MUTEX_LOCK(); + + COAPT_TRC("[%p]: nrf_tls_alloc result %08x", + &m_remote_session[index], + err_code); + + // TLS allocation succeeded, book keep information for endpoint. + if (err_code == NRF_SUCCESS) + { + (*pp_session) = &m_remote_session[index]; + break; + } + else + { + // If free the session and notify failure. + remote_session_init(&m_remote_session[index]); + } + } + } + + return err_code; +} + + +/** + * @brief API to free TLS session. + * + * @param[in] p_session Identifies the session to be freed. + */ +static __INLINE void session_free (coap_remote_session_t * p_session) +{ + // Free TLS session. + UNUSED_VARIABLE(nrf_tls_free(&p_session->dtls_instance)); + + // Free the session. + remote_session_init(p_session); +} + + +/** + * @brief Searches for DTLS session between remote and local endpoint. + * + * @param[in] local_port_index Identifies local endpoint. + * @param[in] p_remote Identifies remote endpoint. + * @param[out] pp_session Pointer to the session found (if any). + * + * @retval NRF_SUCCESS is procedure succeeded else NRF_ERROR_NOT_FOUND. + */ +uint32_t remote_session_search(uint32_t local_port_index, + const coap_remote_t * p_remote, + coap_remote_session_t ** pp_session) +{ + uint32_t err_code = NRF_ERROR_NOT_FOUND; + uint32_t index = 0; + + + for (index = 0; index < COAP_DTLS_MAX_REMOTE_SESSION; index++) + { + const coap_remote_session_t * session = &m_remote_session[index]; + if ((session->local_port_index == local_port_index) && + (session->remote_endpoint.port_number == p_remote->port_number) && + ((memcmp(session->remote_endpoint.addr, p_remote->addr, IPV6_ADDR_SIZE) == 0))) + { + // Entry exists. + (*pp_session) = (coap_remote_session_t *)session; + err_code = NRF_SUCCESS; + break; + } + } + + return err_code; +} + + +/** + * @brief Provides transport type to be used between remote and local endpoint. + * + * @param[in] local_port_index Identifies local endpoint. + * @param[in] p_remote Identifies remote endpoint. + * + * @retval COAP_TRANSPORT_SECURE_DATAGRAM if transport type to be used is secure. + * @retval COAP_TRANSPORT_NON_SECURE_DATAGRAM if transport type to be used is non-secure. + */ +uint8_t get_transport_type(uint32_t local_port_index, const coap_remote_t * p_remote) +{ + coap_remote_session_t * p_session; + uint8_t transport_type = COAP_TRANSPORT_NON_SECURE_DATAGRAM; + + uint32_t err_code = remote_session_search(local_port_index, p_remote, &p_session); + if (err_code == NRF_SUCCESS) + { + COAPT_TRC("Transport type = SECURE"); + transport_type = COAP_TRANSPORT_SECURE_DATAGRAM; + } + else if (m_port_table[local_port_index].p_settings != NULL) + { + COAPT_TRC("Transport type = SECURE"); + transport_type = COAP_TRANSPORT_SECURE_DATAGRAM; + } + else + { + COAPT_TRC("Transport type = NON-SECURE"); + } + + return transport_type; +} + + +/**@brief Callback handler to receive data on the UDP port. + * + * @details Callback handler to receive data on the UDP port. + * + * @param[in] p_socket Socket identifier. + * @param[in] p_ip_header IPv6 header containing source and destination addresses. + * @param[in] p_udp_header UDP header identifying local and remote endpoints. + * @param[in] process_result Result of data reception, there could be possible errors like + * invalid checksum etc. + * @param[in] iot_pbuffer_t Packet buffer containing the received data packet. + * + * @retval NRF_SUCCESS Indicates received data was handled successfully, else an an + * error code indicating reason for failure.. + */ +static uint32_t port_data_callback(const udp6_socket_t * p_socket, + const ipv6_header_t * p_ip_header, + const udp6_header_t * p_udp_header, + uint32_t process_result, + iot_pbuffer_t * p_rx_packet) +{ + uint32_t index; + uint32_t retval = NRF_ERROR_NOT_FOUND; + + COAPT_TRC("port_data_callback: Src Port %d Dest Port %d. Len %08lx", + p_udp_header->srcport, p_udp_header->destport, p_rx_packet->length); + + COAP_MUTEX_LOCK(); + + //Search for the port. + for (index = 0; index < COAP_PORT_COUNT; index++) + { + if (m_port_table[index].socket_id == p_socket->socket_id) + { + COAPT_TRC("port_data_callback->coap_transport_read"); + + //Matching port found. + coap_remote_t remote_endpoint; + + memcpy(remote_endpoint.addr, p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE); + remote_endpoint.port_number = p_udp_header->srcport; + + uint8_t transport_type = get_transport_type(index, &remote_endpoint); + + // Handle read data on scoket based on nature of transport. + retval = port_read_fn[transport_type](&remote_endpoint, + index, + process_result, + p_rx_packet->p_payload, + p_rx_packet->length); + break; + } + } + + COAP_MUTEX_UNLOCK(); + + return retval; +} + + +/** + * @brief Transport read handler for non secure transport type. + * + * @param[in] p_remote Remote endpoint which sent data. + * @param[in] local_port_index Local endpoint identifier to which the data was sent. + * @param[in] read_result Indicator result of data read on the transport. + * Likely failures include UDP checksum failure, truncated packet etc. + * @param[in] p_data Data read on the transport. + * @param[in] datalen Length of data read on the transport. + * + * @retval NRF_SUCCESS if the procedure was successful, else an error code indicating reason for + * failure. + */ +uint32_t non_secure_datagram_read (const coap_remote_t * const p_remote, + uint32_t local_port_index, + uint32_t read_result, + const uint8_t * p_data, + uint16_t datalen) +{ + const coap_port_t port = + { + .port_number = m_port_table[local_port_index].port_number + }; + + return coap_transport_read(&port, + p_remote, + NULL, + read_result, + p_data, + datalen); +} + + +/** + * @brief Transport write handler for non secure transport type. + * + * @param[in] p_remote Remote endpoint on which data is to be written. + * @param[in] local_port_index Local endpoint identifier writing the data. + * @param[in] p_data Data to be written. + * @param[in] datalen Length of data to be written. + * + * @retval NRF_SUCCESS if the procedure was successful, else an error code indicating reason for + * failure. + */ +uint32_t non_secure_datagram_write(const coap_remote_t * p_remote, + uint32_t index, + const uint8_t * p_data, + uint16_t datalen) +{ + uint32_t err_code; + udp6_socket_t socket; + ipv6_addr_t remote_addr; + iot_pbuffer_t * p_buffer; + iot_pbuffer_alloc_param_t buffer_param; + + buffer_param.type = UDP6_PACKET_TYPE; + buffer_param.flags = PBUFFER_FLAG_DEFAULT; + buffer_param.length = datalen; + + COAPT_TRC("[LP %04X]:[RP %04X]: port_write, datalen %d", + m_port_table[index].port_number, + p_remote->port_number, + datalen); + + memcpy(remote_addr.u8, p_remote->addr, IPV6_ADDR_SIZE); + + // Allocate buffer to send the data on port. + err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer); + + COAPT_TRC("port_write->iot_pbuffer_allocate result 0x%08X", err_code); + + if (err_code == NRF_SUCCESS) + { + socket.socket_id = m_port_table[index].socket_id; + + // Make a copy of the data onto the buffer. + memcpy(p_buffer->p_payload, p_data, datalen); + + // Send on UDP port. + err_code = udp6_socket_sendto(&socket, + &remote_addr, + p_remote->port_number, + p_buffer); + + COAPT_TRC("port_write->udp6_socket_sendto result 0x%08X", err_code); + if (err_code != NRF_SUCCESS) + { + // Free the allocated buffer as send procedure has failed. + UNUSED_VARIABLE(iot_pbuffer_free(p_buffer, true)); + } + } + + return err_code; +} + + +/** + * @brief Transport read handler for secure transport type. + * + * @param[in] p_remote Remote endpoint which sent data. + * @param[in] local_port_index Local endpoint identifier to which the data was sent. + * @param[in] read_result Indicator result of data read on the transport. + * Likely failures include UDP checksum failure, truncated packet etc. + * @param[in] p_data Data read on the transport. + * @param[in] datalen Length of data read on the transport. + * + * @retval NRF_SUCCESS if the procedure was successful, else an error code indicating reason for + * failure. + */ +uint32_t secure_datagram_read(const coap_remote_t * const p_remote, + uint32_t local_port_index, + uint32_t read_result, + const uint8_t * p_data, + uint16_t datalen) +{ + const uint32_t read_len = datalen; + uint32_t err_code; + coap_remote_session_t * p_session = NULL; + + COAPT_ENTRY_WITH_READLEN(read_len); + + // Search is a session exists. + err_code = remote_session_search(local_port_index, p_remote, &p_session); + + if (err_code == NRF_SUCCESS) + { + COAPT_TRC("Remote session found, processing."); + + COAP_MUTEX_UNLOCK(); + + // Session exists, send data to DTLS for decryption. + err_code = nrf_tls_input(&p_session->dtls_instance, p_data, read_len); + + COAP_MUTEX_LOCK(); + } + else + { + COAPT_TRC("Remote session not found, look for server security settings."); + if (m_port_table[local_port_index].p_settings != NULL) + { + // Allocate a session for incoming client. + err_code = session_create(local_port_index, + NRF_TLS_ROLE_SERVER, + p_remote, + m_port_table[local_port_index].p_settings, + &p_session); + + if (err_code == NRF_SUCCESS) + { + COAP_MUTEX_UNLOCK(); + + COAPT_TRC("[%p]: New session created as DTLS server.", p_session); + + err_code = nrf_tls_input(&p_session->dtls_instance, p_data, read_len); + + COAP_MUTEX_LOCK(); + } + else + { + COAPT_TRC("New session creation failed, reason 0x%08x.", err_code); + } + } + else + { + COAPT_TRC("No remote session, no server settings, processing as raw"); + err_code = non_secure_datagram_read(p_remote, + local_port_index, + read_result, + p_data, + datalen); + } + } + + COAPT_EXIT_WITH_RESULT(err_code); + + return err_code; +} + + +/** + * @brief Transport write handler for secure transport type. + * + * @param[in] p_remote Remote endpoint on which data is to be written. + * @param[in] local_port_index Local endpoint identifier writing the data. + * @param[in] p_data Data to be written. + * @param[in] datalen Length of data to be written. + * + * @retval NRF_SUCCESS if the procedure was successful, else an error code indicating reason for + * failure. + */ +uint32_t secure_datagram_write(const coap_remote_t * const p_remote, + uint32_t local_port_index, + const uint8_t * p_data, + uint16_t datalen) +{ + + uint32_t err_code; + coap_remote_session_t * p_session; + + // Search is a session exists. + err_code = remote_session_search(local_port_index, p_remote, &p_session); + + if (err_code == NRF_ERROR_NOT_FOUND) + { + // No session found, return error. + err_code = COAP_TRANSPORT_SECURITY_MISSING; + } + else + { + // Session exists, attempt a secure write. + uint32_t actual_len = datalen; + err_code = nrf_tls_write(&p_session->dtls_instance, p_data, &actual_len); + } + + return err_code; +} + + +/** + * @brief Handles DTLS output to be sent on the underlying UDP transport. + * + * @param[in] p_instance Identifies the TLS instance associated with the output. + * @param[in] p_data DTLS library output data to be written on the transport. + * @param[in] datalen Length of data. + * + * @retval NRF_SUCCESS if the procedure was successful, else an error code indicating reason for + * failure. + */ +uint32_t dtls_output_handler(nrf_tls_instance_t const * p_instance, + uint8_t const * p_data, + uint32_t datalen) +{ + const uint16_t transport_write_len = datalen; + + if (p_instance->transport_id >= COAP_DTLS_MAX_REMOTE_SESSION) + { + return NRF_ERROR_NOT_FOUND; + } + + COAP_MUTEX_LOCK(); + + coap_remote_session_t * p_session = &m_remote_session[p_instance->transport_id]; + uint32_t err_code = NRF_ERROR_NOT_FOUND; + + if (p_session->remote_endpoint.port_number != 0) + { + // Search for instance in remote sessions. + err_code = non_secure_datagram_write(&p_session->remote_endpoint, + p_session->local_port_index, + p_data, + transport_write_len); + } + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + + +/**@brief Creates port as requested in p_port. + * + * @details Creates port as requested in p_port. + * + * @param[in] index Index to the m_port_table where entry of the port created is to be made. + * @param[in] p_port Port information to be created. + * + * @retval NRF_SUCCESS Indicates if port was created successfully, else an an error code + * indicating reason for failure. + */ +static uint32_t port_create(uint32_t index, coap_port_t * p_port) +{ + uint32_t err_code; + udp6_socket_t socket; + + // Request new socket creation. + err_code = udp6_socket_allocate(&socket); + + if (err_code == NRF_SUCCESS) + { + // Bind the socket to the local port. + err_code = udp6_socket_bind(&socket, IPV6_ADDR_ANY, p_port->port_number); + + if (err_code == NRF_SUCCESS) + { + // Register data receive callback. + err_code = udp6_socket_recv(&socket, port_data_callback); + + if (err_code == NRF_SUCCESS) + { + // All procedure with respect to port creation succeeded, make entry in the table. + m_port_table[index].socket_id = socket.socket_id; + m_port_table[index].port_number = p_port->port_number; + m_port_table[index].p_settings = NULL; + + socket.p_app_data = &m_port_table[index]; + UNUSED_VARIABLE(udp6_socket_app_data_set(&socket)); + } + } + + if (err_code != NRF_SUCCESS) + { + // Not all procedures succeeded with allocated socket, hence free it. + UNUSED_VARIABLE(udp6_socket_free(&socket)); + } + } + return err_code; +} + + +uint32_t coap_transport_init(const coap_transport_init_t * p_param) +{ + uint32_t err_code = NRF_ERROR_NO_MEM; + uint32_t index; + + NULL_PARAM_CHECK(p_param); + NULL_PARAM_CHECK(p_param->p_port_table); + + err_code = nrf_tls_init(); + + if (err_code == NRF_SUCCESS) + { + for (index = 0; index < COAP_PORT_COUNT; index++) + { + // Create end point for each of the CoAP ports. + err_code = port_create(index, &p_param->p_port_table[index]); + if (err_code != NRF_SUCCESS) + { + break; + } + } + + for (index = 0; index < COAP_DTLS_MAX_REMOTE_SESSION; index++) + { + remote_session_init(&m_remote_session[index]); + } + } + + return err_code; +} + + +uint32_t coap_transport_write(const coap_port_t * p_port, + const coap_remote_t * p_remote, + const uint8_t * p_data, + uint16_t datalen) +{ + uint32_t err_code = NRF_ERROR_NOT_FOUND; + uint32_t index; + + NULL_PARAM_CHECK(p_port); + NULL_PARAM_CHECK(p_remote); + NULL_PARAM_CHECK(p_data); + + COAP_MUTEX_LOCK(); + + //Search for the corresponding port. + for (index = 0; index < COAP_PORT_COUNT; index++) + { + if (m_port_table[index].port_number == p_port->port_number) + { + // Get transport type for remote and local. + uint8_t transport_type = get_transport_type(index, p_remote); + + err_code = port_write_fn[transport_type](p_remote, + index, + p_data, + datalen); + break; + } + } + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + + +void coap_transport_process(void) +{ + nrf_tls_process(); + + for (uint32_t index = 0; index < COAP_DTLS_MAX_REMOTE_SESSION; index++) + { + coap_remote_session_t * p_session = &m_remote_session[index]; + + if (p_session->remote_endpoint.port_number != 0x0000) + { + uint32_t datalen = MAX_BUFFER_SIZE; + + COAP_MUTEX_UNLOCK(); + + // Check if there is data to be processed/read. + uint32_t err_code = nrf_tls_read(&p_session->dtls_instance, NULL, &datalen); + + COAP_MUTEX_LOCK(); + + COAPT_TRC("nrf_tls_read result %d", err_code); + + if ((err_code == NRF_SUCCESS) && (datalen > 0)) + { + COAPT_TRC("nrf_tls_read datalen %d", datalen); + + // Allocate memory and fecth data from DTLS layer. + uint8_t * p_data = NULL; + uint32_t buffer_size = datalen; + + err_code = nrf_mem_reserve(&p_data, &buffer_size); + + if (p_data != NULL) + { + COAP_MUTEX_UNLOCK(); + + err_code = nrf_tls_read(&p_session->dtls_instance, p_data, &datalen); + + COAP_MUTEX_LOCK(); + + if ((err_code == NRF_SUCCESS) && (datalen > 0)) + { + UNUSED_VARIABLE(non_secure_datagram_read(&p_session->remote_endpoint, + p_session->local_port_index, + NRF_SUCCESS, + p_data, + datalen)); + } + + // Free the memory reserved for the incoming packet. + nrf_free(p_data); + } + } + } + } +} + + +uint32_t coap_security_setup(uint16_t local_port, + nrf_tls_role_t role, + coap_remote_t * const p_remote, + nrf_tls_key_settings_t * const p_settings) +{ + uint32_t err_code = NRF_ERROR_NO_MEM; + uint32_t local_port_index; + coap_remote_session_t * p_session; + + COAP_MUTEX_LOCK(); + + // Find local port index. + err_code = local_port_index_get(&local_port_index, local_port); + + if (err_code == NRF_SUCCESS) + { + if (role == NRF_TLS_ROLE_CLIENT) + { + if (p_remote == NULL) + { + // Clients can never register session with wildcard remote. + err_code = NRF_ERROR_NULL; + } + else + { + // Search is a session exists. + err_code = remote_session_search(local_port_index, p_remote, &p_session); + + if (err_code != NRF_SUCCESS) + { + // Session does not already exist, create one. + err_code = session_create(local_port_index, + role, p_remote, + p_settings, + &p_session); + } + } + } + else + { + // For server role, disallow client specific settings. + // This may not always be desired. But a constraint added in current design. + if (p_remote != NULL) + { + err_code = NRF_ERROR_INVALID_PARAM; + } + else + { + // Cannot overwrite settings. + if (m_port_table[local_port_index].p_settings != NULL) + { + err_code = NRF_ERROR_FORBIDDEN; + } + else + { + COAPT_TRC("[0x%08lx]: Server security setup successful", + local_port_index); + + m_port_table[local_port_index].p_settings = p_settings; + } + } + + } + } + + COAP_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t coap_security_destroy(uint16_t local_port, + coap_remote_t * const p_remote) +{ + uint32_t err_code; + coap_remote_session_t * p_session; + uint32_t local_port_index; + + COAP_MUTEX_LOCK(); + + // Find local port index. + err_code = local_port_index_get(&local_port_index, local_port); + + if (err_code == NRF_SUCCESS) + { + if (p_remote != NULL) + { + // Search is a session exists. + err_code = remote_session_search(local_port_index, p_remote, &p_session); + + if (err_code == NRF_SUCCESS) + { + session_free(p_session); + } + } + else + { + // Remove all session associated with the local port if p_remote is NULL. + for (uint32_t index = 0; index < COAP_DTLS_MAX_REMOTE_SESSION ; index++) + { + if (m_remote_session[index].local_port_index == local_port_index) + { + session_free(&m_remote_session[index]); + } + } + } + } + + COAP_MUTEX_UNLOCK(); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_ipv6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_ipv6.c new file mode 100644 index 0000000..9cf4bdb --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_ipv6.c @@ -0,0 +1,264 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_errors.h" +#include "sdk_config.h" +#include "iot_common.h" +#include "iot_pbuffer.h" +#include "coap_transport.h" +#include "coap.h" +#include "udp_api.h" + + +/**@brief UDP port information. */ +typedef struct +{ + uint32_t socket_id; /**< Socket information provided by UDP. */ + uint16_t port_number; /**< Associated port number. */ +} udp_port_t; + +static udp_port_t m_port_table[COAP_PORT_COUNT]; /**< Table maintaining association between CoAP ports and corresponding UDP socket identifiers. */ + + +/**@brief Callback handler to receive data on the UDP port. + * + * @details Callback handler to receive data on the UDP port. + * + * @param[in] p_socket Socket identifier. + * @param[in] p_ip_header IPv6 header containing source and destination addresses. + * @param[in] p_udp_header UDP header identifying local and remote endpoints. + * @param[in] process_result Result of data reception, there could be possible errors like + * invalid checksum etc. + * @param[in] iot_pbuffer_t Packet buffer containing the received data packet. + * + * @retval NRF_SUCCESS Indicates received data was handled successfully, else an an + * error code indicating reason for failure.. + */ +static uint32_t port_data_callback(const udp6_socket_t * p_socket, + const ipv6_header_t * p_ip_header, + const udp6_header_t * p_udp_header, + uint32_t process_result, + iot_pbuffer_t * p_rx_packet) +{ + uint32_t index; + uint32_t retval = NRF_ERROR_NOT_FOUND; + + //Search for the port. + for (index = 0; index < COAP_PORT_COUNT; index++) + { + //Matching port found. + if (m_port_table[index].socket_id == p_socket->socket_id) + { + coap_remote_t remote_endpoint; + coap_remote_t local_endpoint; + const coap_port_t port = {p_udp_header->destport}; + + memcpy (remote_endpoint.addr, p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE); + remote_endpoint.port_number = p_udp_header->srcport; + + memcpy (local_endpoint.addr, p_ip_header->destaddr.u8, IPV6_ADDR_SIZE); + local_endpoint.port_number = p_udp_header->destport; + + COAP_MUTEX_LOCK(); + + //Notify the module of received data. + retval = coap_transport_read(&port, + &remote_endpoint, + &local_endpoint, + process_result, + p_rx_packet->p_payload, + p_rx_packet->length); + + COAP_MUTEX_UNLOCK(); + } + } + + return retval; +} + + +/**@brief Creates port as requested in p_port. + * + * @details Creates port as requested in p_port. + * + * @param[in] index Index to the m_port_table where entry of the port created is to be made. + * @param[in] p_port Port information to be created. + * + * @retval NRF_SUCCESS Indicates if port was created successfully, else an an error code + * indicating reason for failure. + */ +static uint32_t port_create(uint32_t index, coap_port_t * p_port) +{ + uint32_t err_code; + udp6_socket_t socket; + + //Request new socket creation. + err_code = udp6_socket_allocate(&socket); + + if (err_code == NRF_SUCCESS) + { + // Bind the socket to the local port. + err_code = udp6_socket_bind(&socket, IPV6_ADDR_ANY, p_port->port_number); + if (err_code == NRF_SUCCESS) + { + //Register data receive callback. + err_code = udp6_socket_recv(&socket, port_data_callback); + if (err_code == NRF_SUCCESS) + { + //All procedure with respect to port creation succeeded, make entry in the table. + m_port_table[index].socket_id = socket.socket_id; + m_port_table[index].port_number = p_port->port_number; + socket.p_app_data = &m_port_table[index]; + UNUSED_VARIABLE(udp6_socket_app_data_set(&socket)); + } + } + + if (err_code != NRF_SUCCESS) + { + //Not all procedures succeeded with allocated socket, hence free it. + UNUSED_VARIABLE(udp6_socket_free(&socket)); + } + } + return err_code; +} + + +uint32_t coap_transport_init(const coap_transport_init_t * p_param) +{ + uint32_t err_code = NRF_ERROR_NO_MEM; + uint32_t index; + + NULL_PARAM_CHECK(p_param); + NULL_PARAM_CHECK(p_param->p_port_table); + + for (index = 0; index < COAP_PORT_COUNT; index++) + { + // Create end point for each of the CoAP ports. + err_code = port_create(index, &p_param->p_port_table[index]); + if (err_code != NRF_SUCCESS) + { + break; + } + } + + return err_code; +} + + +uint32_t coap_transport_write(const coap_port_t * p_port, + const coap_remote_t * p_remote, + const uint8_t * p_data, + uint16_t datalen) +{ + uint32_t err_code = NRF_ERROR_NOT_FOUND; + uint32_t index; + udp6_socket_t socket; + ipv6_addr_t remote_addr; + iot_pbuffer_t * p_buffer; + iot_pbuffer_alloc_param_t buffer_param; + + NULL_PARAM_CHECK(p_port); + NULL_PARAM_CHECK(p_remote); + NULL_PARAM_CHECK(p_data); + + memcpy(remote_addr.u8, p_remote->addr, 16); + + + buffer_param.type = UDP6_PACKET_TYPE; + buffer_param.flags = PBUFFER_FLAG_DEFAULT; + buffer_param.length = datalen; + + //Search for the corresponding port. + for (index = 0; index < COAP_PORT_COUNT; index ++) + { + if (m_port_table[index].port_number == p_port->port_number) + { + //Allocate buffer to send the data on port. + err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer); + + if (err_code == NRF_SUCCESS) + { + socket.socket_id = m_port_table[index].socket_id; + + //Make a copy of the data onto the buffer. + memcpy (p_buffer->p_payload, p_data, datalen); + + COAP_MUTEX_UNLOCK(); + + //Send on UDP port. + err_code = udp6_socket_sendto(&socket, + &remote_addr, + p_remote->port_number, + p_buffer); + + COAP_MUTEX_LOCK(); + + if (err_code != NRF_SUCCESS) + { + //Free the allocated buffer as send procedure has failed. + UNUSED_VARIABLE(iot_pbuffer_free(p_buffer, true)); + } + } + break; + } + } + + return err_code; +} + + +void coap_transport_process(void) +{ + return; +} + +uint32_t coap_security_setup(uint16_t local_port, + nrf_tls_role_t role, + coap_remote_t * const p_remote, + nrf_tls_key_settings_t * const p_settings) +{ + return NRF_ERROR_API_NOT_IMPLEMENTED; +} + + +uint32_t coap_security_destroy(uint16_t local_port, + coap_remote_t * const p_remote) +{ + return NRF_ERROR_API_NOT_IMPLEMENTED; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_lwip.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_lwip.c new file mode 100644 index 0000000..6245a97 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_lwip.c @@ -0,0 +1,255 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdint.h> +#include "sdk_errors.h" +#include "sdk_config.h" +#include "iot_common.h" +#include "coap_transport.h" +#include "coap.h" +#include "lwip/ip6_addr.h" +/*lint -save -e607 Suppress warning 607 "Parameter p of macro found within string" */ +#include "lwip/udp.h" +/*lint -restore */ + + +/**@brief UDP port information. */ +typedef struct +{ + struct udp_pcb * p_socket; /**< Socket information provided by UDP. */ + uint16_t port_number; /**< Associated port number. */ +}udp_port_t; + +static udp_port_t m_port_table[COAP_PORT_COUNT]; /**< Table maintaining association between CoAP ports and corresponding UDP socket identifiers. */ + + +/**@brief Callback handler to receive data on the UDP port. + * + * @details Callback handler to receive data on the UDP port. + * + * @param[in] p_arg Receive argument associated with the UDP socket. + * @param[in] p_socket Socket identifier. + * @param[in] p_ip_header IPv6 header containing source and destination addresses. + * @param[in] p_remote_addr IPv6 address of the remote device. + * @param[in] port Port number identifying the remote endpoint. + * + * @retval NRF_SUCCESS Indicates received data was handled successfully, else an an + * error code indicating reason for failure. + */ +static void udp_recv_data_handler(void * p_arg, + struct udp_pcb * p_socket, + struct pbuf * p_buffer, + const ip6_addr_t * p_remote_addr, + u16_t port) +{ + uint32_t index; + coap_remote_t remote_endpoint; + coap_port_t local_port = {p_socket->local_port}; + + for (index = 0; index < COAP_PORT_COUNT; index++) + { + if (m_port_table[index].p_socket == p_socket) + { + memcpy (remote_endpoint.addr, p_remote_addr, 16); + remote_endpoint.port_number = port; + + COAP_MUTEX_LOCK(); + + UNUSED_VARIABLE(coap_transport_read(&local_port, + &remote_endpoint, + NULL, + NRF_SUCCESS, + (uint8_t *)p_buffer->payload, + (uint32_t)p_buffer->len)); + + COAP_MUTEX_UNLOCK(); + + break; + } + } + + //Freeing packet (irrespective of matching p_socket is found or not + //to avoid memory leaks in the system. + UNUSED_VARIABLE(pbuf_free(p_buffer)); +} + + +/**@brief Creates port as requested in p_port. + * + * @details Creates port as requested in p_port. + * + * @param[in] index Index to the m_port_table where entry of the port created is to be made. + * @param[in] p_port Port information to be created. + * + * @retval NRF_SUCCESS Indicates if port was created successfully, else an an error code + * indicating reason for failure. + */ +static uint32_t port_create(uint32_t index, coap_port_t * p_port) +{ + err_t err = NRF_ERROR_NO_MEM; + ip6_addr_t any_addr; + struct udp_pcb * p_socket = m_port_table[index].p_socket; + + ip6_addr_set_any(&any_addr); + + //Request new socket creation. + p_socket = udp_new(); + + if (NULL != p_socket) + { + // Bind the socket to the local port. + err = udp_bind(p_socket, &any_addr, p_port->port_number); + if (err == ERR_OK) + { + //Register data receive callback. + udp_recv(p_socket, udp_recv_data_handler, &m_port_table[index]); + //All procedure with respect to port creation succeeded, make entry in the table. + m_port_table[index].port_number = p_port->port_number; + m_port_table[index].p_socket = p_socket; + } + else + { + //Not all procedures succeeded with allocated socket, hence free it. + err = NRF_ERROR_INVALID_PARAM; + udp_remove(p_socket); + } + } + + return err; +} + + +uint32_t coap_transport_init(const coap_transport_init_t * p_param) +{ + uint32_t err_code = NRF_SUCCESS; + uint32_t index; + + NULL_PARAM_CHECK(p_param); + NULL_PARAM_CHECK(p_param->p_port_table); + + for (index = 0; index < COAP_PORT_COUNT; index++) + { + // Create end point for each of the COAP ports. + err_code = port_create(index, &p_param->p_port_table[index]); + if (err_code != NRF_SUCCESS) + { + break; + } + } + + return err_code; +} + + +uint32_t coap_transport_write(const coap_port_t * p_port, + const coap_remote_t * p_remote, + const uint8_t * p_data, + uint16_t datalen) +{ + + err_t err = NRF_ERROR_NOT_FOUND; + uint32_t index; + + NULL_PARAM_CHECK(p_port); + NULL_PARAM_CHECK(p_remote); + NULL_PARAM_CHECK(p_data); + + //Search for the corresponding port. + for (index = 0; index < COAP_PORT_COUNT; index++) + { + if (m_port_table[index].port_number == p_port->port_number) + { + //Allocate Buffer to send the data on port. + struct pbuf * lwip_buffer = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM); + + if (NULL != lwip_buffer) + { + //Make a copy of the data onto the buffer. + memcpy(lwip_buffer->payload, p_data, datalen); + + COAP_MUTEX_UNLOCK(); + + //Send on UDP port. + err = udp_sendto(m_port_table[index].p_socket, + lwip_buffer, + (ip6_addr_t *)p_remote->addr, + p_remote->port_number); + + COAP_MUTEX_LOCK(); + + + if (err != ERR_OK) + { + //Free the allocated buffer as send procedure has failed. + err = NRF_ERROR_INTERNAL; + } + UNUSED_VARIABLE(pbuf_free(lwip_buffer)); + } + else + { + //Buffer allocation failed, cannot send data. + err = NRF_ERROR_NO_MEM; + } + break; + } + } + return err; +} + + +void coap_transport_process(void) +{ + return; +} + + +uint32_t coap_security_setup(uint16_t local_port, + nrf_tls_role_t role, + coap_remote_t * const p_remote, + nrf_tls_key_settings_t * const p_settings) +{ + return NRF_ERROR_API_NOT_IMPLEMENTED; +} + + +uint32_t coap_security_destroy(uint16_t local_port, + coap_remote_t * const p_remote) +{ + return NRF_ERROR_API_NOT_IMPLEMENTED; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_socket.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_socket.c new file mode 100644 index 0000000..d19737f --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/coap/coap_transport_socket.c @@ -0,0 +1,292 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <sys/socket.h> +#include <sys/select.h> +#include <netinet/in.h> +#ifdef UNIX +#include <fcntl.h> +#endif +#include <errno.h> +#include <stdint.h> +#include "mem_manager.h" +#include "sdk_errors.h" +#include "sdk_config.h" +#include "iot_common.h" +#include "nordic_common.h" +#include "coap_transport.h" +#include "coap.h" + +/**@brief UDP port information. */ +typedef struct +{ + int socket_fd; /**< Socket information provided by UDP. */ + uint16_t port_number; /**< Associated port number. */ +} udp_port_t; + +static udp_port_t m_port_table[COAP_PORT_COUNT]; /**< Table maintaining association between CoAP ports and corresponding UDP socket identifiers. */ + +static fd_set m_readfds; +static int m_max_sd = 0; + +/**@brief Creates port as requested in p_port. + * + * @details Creates port as requested in p_port. + * + * @param[in] index Index to the m_port_table where entry of the port created is to be made. + * @param[in] p_port Port information to be created. + * + * @retval NRF_SUCCESS Indicates if port was created successfully, else an an error code + * indicating reason for failure. + */ +static uint32_t port_create(uint32_t index, coap_port_t * p_port) +{ + // Request new socket creation. + int socket_fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + + if (socket_fd != -1) + { + // Bind the socket to the local port. + struct sockaddr_in6 sin6; + + memset(&sin6, 0, sizeof(struct sockaddr_in6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(p_port->port_number); + sin6.sin6_addr = in6addr_any; + + int retval = bind(socket_fd, (struct sockaddr *)&sin6, sizeof(sin6)); + if (retval != -1) + { + m_port_table[index].port_number = p_port->port_number; + m_port_table[index].socket_fd = socket_fd; + } + else + { + // Not all procedures succeeded with allocated socket, hence free it. + UNUSED_VARIABLE(close(socket_fd)); + return NRF_ERROR_INVALID_PARAM; + } + } + + // Configure socket to be non-blocking. + int flags = fcntl(socket_fd, F_GETFL, 0); + flags |= O_NONBLOCK; + UNUSED_VARIABLE(fcntl(m_port_table[index].socket_fd, F_SETFL, flags)); + + // Add socket to file descriptor set. + FD_SET(m_port_table[index].socket_fd, &m_readfds); + + // If enumeration is having a gap, increase the max fd count. + if (socket_fd >= m_max_sd) + { + m_max_sd = (socket_fd + 1); + } + + return NRF_SUCCESS; +} + + +uint32_t coap_transport_init(const coap_transport_init_t * p_param) +{ + uint32_t err_code = NRF_SUCCESS; + uint32_t index; + + NULL_PARAM_CHECK(p_param); + NULL_PARAM_CHECK(p_param->p_port_table); + + FD_ZERO(&m_readfds); + + err_code = nrf_mem_init(); + + if (err_code == NRF_SUCCESS) { + for (index = 0; index < COAP_PORT_COUNT; index++) + { + // Create end point for each of the COAP ports. + err_code = port_create(index, &p_param->p_port_table[index]); + if (err_code != NRF_SUCCESS) + { + break; + } + } + } + + return err_code; +} + + +uint32_t coap_transport_write(const coap_port_t * p_port, + const coap_remote_t * p_remote, + const uint8_t * p_data, + uint16_t datalen) +{ + + uint32_t err_code = NRF_ERROR_NOT_FOUND; + uint32_t index; + + NULL_PARAM_CHECK(p_port); + NULL_PARAM_CHECK(p_remote); + NULL_PARAM_CHECK(p_data); + + // Search for the corresponding port. + for (index = 0; index < COAP_PORT_COUNT; index++) + { + if (m_port_table[index].port_number == p_port->port_number) + { + COAP_MUTEX_UNLOCK(); + + static struct sockaddr_in6 dest_address_in6; + + memset(&dest_address_in6, 0, sizeof(struct sockaddr_in6)); + dest_address_in6.sin6_family = AF_INET6; + dest_address_in6.sin6_port = htons(p_remote->port_number); + + memcpy(&dest_address_in6.sin6_addr, p_remote->addr, sizeof(struct in6_addr)); + + // Send on UDP port. + int retval = sendto(m_port_table[index].socket_fd, + p_data, + datalen, + 0, + (struct sockaddr *)&dest_address_in6, + sizeof(dest_address_in6)); + if (retval == -1) + { + // Error in sendto. + err_code = NRF_ERROR_INTERNAL; + } + else + { + err_code = NRF_SUCCESS; + } + + COAP_MUTEX_LOCK(); + break; + } + } + + return err_code; +} + + +void coap_transport_process(void) +{ + return; +} + + +uint32_t coap_security_setup(uint16_t local_port, + nrf_tls_role_t role, + coap_remote_t * const p_remote, + nrf_tls_key_settings_t * const p_settings) +{ + return SDK_ERR_API_NOT_IMPLEMENTED; +} + + +uint32_t coap_security_destroy(uint16_t local_port, + coap_remote_t * const p_remote) +{ + return SDK_ERR_API_NOT_IMPLEMENTED; +} + + +void coap_transport_input(void) +{ + int retval = select(m_max_sd, &m_readfds, NULL, NULL, NULL); + + if (retval == -1) + { + // Error in select(). + // Placeholder for debugging. + } + else if (retval >= 1) // Number of file descriptiors with activity. + { + uint32_t index = 0; + int socket_fd = m_port_table[index].socket_fd; + + // The descriptor has data. + if (FD_ISSET(socket_fd, &m_readfds)) // If socket_fd is set to read. + { + static uint8_t read_mem[COAP_MESSAGE_DATA_MAX_SIZE]; + static struct sockaddr_in6 client_address_in6; + socklen_t address_length = sizeof(struct sockaddr_in6); + + int bytes_read = recvfrom(socket_fd, + read_mem, + sizeof(read_mem), + 0, + (struct sockaddr *)&client_address_in6, + (socklen_t *)&address_length); // Blocking call, waiting for incoming transaction. + if (bytes_read >= 0) + { + coap_port_t port; + port.port_number = m_port_table[index].port_number; + + coap_remote_t remote_endpoint; + memcpy(remote_endpoint.addr, &client_address_in6.sin6_addr, sizeof(struct in6_addr)); + remote_endpoint.port_number = ntohs(client_address_in6.sin6_port); + + uint32_t result = NRF_SUCCESS; + + // Notify the CoAP module of received data. + retval = coap_transport_read(&port, + &remote_endpoint, + NULL, + result, + read_mem, + (uint16_t)bytes_read); + + // Nothing much to do if CoAP could not interpret the datagram. + UNUSED_VARIABLE(retval); + } + else + { + // Error in readfrom(). + // Placeholder for debugging. + // If select() indicated this socket file descriptor to have pending + // data, this case should not occur. + } + } + } + else + { + // In case of socket() returning 0, timeout. + // Not implemented. + } +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/common/iot_common.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/common/iot_common.h new file mode 100644 index 0000000..58b653e --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/common/iot_common.h @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file iot_common.h + * + * @defgroup iot_common Common utils + * @ingroup iot_sdk_common + * @{ + * @brief Common IoT macros that are needed by IoT modules. + * + * @details This module abstracts common macros related to IoT. These macros can be used by all IoT modules. + */ + +#ifndef IOT_COMMON_H__ +#define IOT_COMMON_H__ + +#include <stdint.h> +#include <stdbool.h> +#include "iot_defines.h" +#include "iot_errors.h" +#include "sdk_os.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Context identifiers used in stateful compression. */ +typedef struct +{ + uint8_t src_cntxt_id; /**< Source context identifier (if any). IPV6_CONTEXT_IDENTIFIER_NONE by default. IP Stack can set this on the interface if needed. */ + uint8_t dest_cntxt_id; /**< Destination context identifier (if any). IPV6_CONTEXT_IDENTIFIER_NONE by default. IP Stack can set this on the interface if needed. */ +} iot_context_id_t; + +/**@brief Context structure used for efficient compression and decompression. */ +typedef struct +{ + uint8_t context_id; /**< Context identifier (CID) number. */ + uint8_t prefix_len; /**< Length of prefix for CID. */ + ipv6_addr_t prefix; /**< Prefix for CID. */ + bool compression_flag; /**< Indicates if the context can be used for compression. */ +} iot_context_t; + +/**@brief Encapsulates all information of the 6LoWPAN interface. */ +typedef struct +{ + eui64_t local_addr; /**< Local EUI-64 address. */ + eui64_t peer_addr; /**< Peer EUI-64 address. */ + iot_context_id_t tx_contexts; /**< TX contexts can be used for effective compression. */ + void * p_upper_stack; /**< Block to upper stack. */ + void * p_transport; /**< Transport reference. */ +} iot_interface_t; + +#ifdef __cplusplus +} +#endif + +#endif //IOT_COMMON_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/common/iot_defines.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/common/iot_defines.h new file mode 100644 index 0000000..e8c087a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/common/iot_defines.h @@ -0,0 +1,321 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file iot_defines.h + * + * @defgroup iot_defines IoT Defines + * @ingroup iot_sdk_common + * @{ + * @brief Common IoT definitions that are needed by IoT modules. + * + * @details This module abstracts common data structures and constants related to IoT. + * These definitions can be used by all the IoT modules. + */ + +#ifndef IOT_DEFINES_H__ +#define IOT_DEFINES_H__ + +#include <stdint.h> +#include <sdk_config.h> +#include <nrf.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Host to network byte-orders on half word. */ +//lint -emacro((572),HTONS) // Suppress warning 572 "Excessive shift value" +#define HTONS(val) ((uint16_t)((((val) & 0xff00) >> 8) | ((((val) & 0x00ff) << 8)))) + +/**@brief Host to network byte-orders on full word. */ +//lint -emacro((572),HTONL) // Suppress warning 572 "Excessive shift value" +#define HTONL(val) ((((uint32_t) (val) & 0xff000000) >> 24) | \ + (((uint32_t) (val) & 0x00ff0000) >> 8) | \ + (((uint32_t) (val) & 0x0000ff00) << 8) | \ + (((uint32_t) (val) & 0x000000ff) << 24)) + +/**@brief Network to host byte-orders on half word. */ +#define NTOHS(val) HTONS(val) + +/**@brief Network to host byte-orders on full word. */ +#define NTOHL(val) HTONL(val) + +#if defined(NRF52) || defined(NRF52_SERIES) + +#define EUI_64_ADDR_SIZE 8 /**< Size of EUI-64. */ +#define IPV6_ADDR_SIZE 16 /**< Size of IPv6 128-bit address. */ +#define IPV6_CONTEXT_IDENTIFIER_NONE 0xFF /**< No context identifier in use. */ + +#if (BLE_6LOWPAN_LEGACY_MODE == 1) +#define IPV6_IID_FLIP_VALUE 0x02 /**< Value XORed with the first byte of EUI-64 to get IID. In some older Linux implementation, this value could be 0x03. */ +#define IPV6_LL_ADDR_SIZE 8 /**< The link-layer address size used in Neighbor Discovery messages. */ +#else +#define IPV6_IID_FLIP_VALUE 0x00 /**< RFC 7668 specifies that no bit is flipped when IID is generated from a Bluetooth Device Address. */ +#define IPV6_LL_ADDR_SIZE 6 /**< The link-layer address size used in Neighbor Discovery messages. */ +#endif + +#define IPV6_IP_HEADER_SIZE 40 /**< IPv6 header size. */ +#define ICMP6_HEADER_SIZE 8 /**< ICMP header size. */ +#define UDP_HEADER_SIZE 8 /**< UDP header size. */ +#define COAP_HEADER_SIZE 4 /**< CoAP header size. */ + +#define IPV6_DEFAULT_VER_TC 0x60 /**< Default value of version and traffic class fields in IPv6 header. */ +#define IPV6_DEFAULT_TC_FL 0x00 /**< Default value of traffic class and flow label fields in IPv6 header. */ +#define IPV6_DEFAULT_FL 0x00 /**< Default value of the flow label's two last bytes in IPv6 header. */ + +#define IPV6_NEXT_HEADER_TCP 6 /**< TCP: protocol number. */ +#define IPV6_NEXT_HEADER_UDP 17 /**< UDP: protocol number. */ +#define IPV6_NEXT_HEADER_ICMP6 58 /**< ICMPv6: protocol number. */ +#define IPV6_NEXT_HEADER_RESERVED 255 /**< Reserved value. */ + +/**@defgroup icmp6_type ICMPv6 message types. + * @ingroup iot_defines + * @{ + */ + +/**@defgroup icmp6_error_type ICMPv6 error messages. + * @ingroup icmp6_type + * @{ + */ +#define ICMP6_TYPE_DESTINATION_UNREACHABLE 1 /**< ICMPv6: Destination unreachable error message. */ +#define ICMP6_TYPE_PACKET_TOO_LONG 2 /**< ICMPv6: Packet too long error message. */ +#define ICMP6_TYPE_TIME_EXCEED 3 /**< ICMPv6: Time-out error message. */ +#define ICMP6_TYPE_PARAMETER_PROBLEM 4 /**< ICMPv6: Parameter problem error message. */ +/** @} */ +#define ICMP6_TYPE_ECHO_REQUEST 128 /**< ICMPv6: Echo request message. */ +#define ICMP6_TYPE_ECHO_REPLY 129 /**< ICMPv6: Echo reply message. */ +#define ICMP6_TYPE_ROUTER_SOLICITATION 133 /**< ICMPv6: Neighbor discovery, router solicitation message. */ +#define ICMP6_TYPE_ROUTER_ADVERTISEMENT 134 /**< ICMPv6: Neighbor discovery, router advertisement message. */ +#define ICMP6_TYPE_NEIGHBOR_SOLICITATION 135 /**< ICMPv6: Neighbor discovery, neighbor solicitation message. */ +#define ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT 136 /**< ICMPv6: Neighbor discovery, neighbor advertisement message. */ +/** @} */ + +/**@brief Initializes IPv6 address. */ +#define IPV6_ADDRESS_INITIALIZE(ADDR) \ + memset((ADDR)->u8, 0, IPV6_ADDR_SIZE) + +/**@brief Checks if prefixes match. Length in bits. */ +#define IPV6_ADDRESS_PREFIX_CMP(prefix, prefix2, length) \ + ((0 == memcmp(prefix, prefix2, (length>>3) - ((length & 0x7) ? 1 : 0) )) && \ + (((prefix[(length>>3)] & (((0xff00) >> (length & 0x7))))) == \ + (prefix2[(length>>3)] & (((0xff00) >> (length & 0x7))))) \ + ) + +/**@brief Sets address prefix. Length in bits. */ +#define IPV6_ADDRESS_PREFIX_SET(pfx_to, pfx_from, length) \ + do { \ + memcpy(pfx_to, pfx_from, length>>3); \ + if (length & 0x7) { \ + uint8_t mask = ((0xff00) >> (length & 0x7)); \ + uint8_t last = pfx_from[length>>3] & mask; \ + pfx_to[length>>3] &= ~mask; \ + pfx_to[length>>3] |= last; \ + } \ + } while (0) + +/**@brief Creates EUI-64 address from EUI-48. + */ +#define IPV6_EUI64_CREATE_FROM_EUI48(eui64, eui48, addr_type) \ + eui64[0] = eui48[5]; \ + eui64[1] = eui48[4]; \ + eui64[2] = eui48[3]; \ + eui64[3] = 0xFF; \ + eui64[4] = 0xFE; \ + eui64[5] = eui48[2]; \ + eui64[6] = eui48[1]; \ + eui64[7] = eui48[0]; \ + if ((addr_type) == BLE_GAP_ADDR_TYPE_PUBLIC) \ + { \ + eui64[0] &= ~(IPV6_IID_FLIP_VALUE); \ + } \ + else \ + { \ + eui64[0] |= IPV6_IID_FLIP_VALUE; \ + } + +/**@brief Creates link-local address from EUI-64. */ +#define IPV6_CREATE_LINK_LOCAL_FROM_EUI64(addr, eui64) \ + (addr)->u32[0] = HTONL(0xFE800000); \ + (addr)->u32[1] = 0; \ + memcpy((addr)->u8 + 8, eui64, EUI_64_ADDR_SIZE); \ + (addr)->u8[8] ^= IPV6_IID_FLIP_VALUE; + +/**@brief Checks if address is a link-local address. */ +#define IPV6_ADDRESS_IS_LINK_LOCAL(addr) \ + ((addr)->u16[0] == HTONS(0xfe80)) + +/**@brief Checks if address is a multicast address. */ +#define IPV6_ADDRESS_IS_MULTICAST(addr) \ + ((addr)->u8[0] == 0xff) + +/**@brief Checks if address is a multicast all-node address. */ +#define IPV6_ADDRESS_IS_ALL_NODE(addr) \ + (((addr)->u32[0] == HTONL(0xff020000)) && \ + ((addr)->u32[1] == 0) && \ + ((addr)->u32[2] == 0) && \ + ((addr)->u32[3] == HTONL(0x01))) + +/**@brief Checks if address is a multicast all-router address. */ +#define IPV6_ADDRESS_IS_ALL_ROUTER(addr) \ + (((addr)->u32[0] == HTONL(0xff020000)) && \ + ((addr)->u32[1] == 0) && \ + ((addr)->u32[2] == 0) && \ + ((addr)->u32[3] == HTONL(0x02))) + +/**@brief Checks if address is a multicast MLDv2 address. */ +#define IPV6_ADDRESS_IS_MLDV2_MCAST(addr) \ + (((addr)->u32[0] == HTONL(0xff020000)) && \ + ((addr)->u32[1] == 0) && \ + ((addr)->u32[2] == 0) && \ + ((addr)->u32[3] == HTONL(0x16))) + +/**@brief Checks if address is a multicast all-node address. */ +#define IPV6_ADDRESS_IS_MULTICAST_SOLICITED_NODE(addr) \ + (((addr)->u32[0] == HTONL(0xff020000)) && \ + ((addr)->u32[1] == 0) && \ + ((addr)->u32[2] == HTONL(0x00000001)) && \ + ((addr)->u8[12] == 0xFF)) + +/**@brief Checks if address is an unspecified address. */ +#define IPV6_ADDRESS_IS_UNSPECIFIED(addr) \ + (((addr)->u32[0] == 0) && \ + ((addr)->u32[1] == 0) && \ + ((addr)->u32[2] == 0) && \ + ((addr)->u32[3] == 0) \ + ) + +/**@brief Compares two IPv6 addresses. */ +#define IPV6_ADDRESS_CMP(addr1, addr2) \ + memcmp((addr1)->u8, (addr2)->u8, IPV6_ADDR_SIZE) + +/**@brief Swaps two IPv6 addresses. */ +#define IPV6_ADDRESS_SWAP(addr1, addr2) \ + do { \ + ipv6_addr_t addr_temp; \ + \ + addr_temp = *addr1; \ + *addr1 = *addr2; \ + *addr2 = addr_temp; \ + } while (0); + +/**@brief Prints an IPV6 address. */ +#define IPV6_ADDRESS_LOG(addr) \ + NRF_LOG_RAW_INFO("%02x%02x:%02x%02x:",(addr).u8[0],(addr).u8[1],(addr).u8[2],(addr).u8[3]); \ + NRF_LOG_RAW_INFO("%02x%02x:%02x%02x:",(addr).u8[4],(addr).u8[5],(addr).u8[6],(addr).u8[7]); \ + NRF_LOG_RAW_INFO("%02x%02x:%02x%02x:",(addr).u8[8],(addr).u8[9],(addr).u8[10],(addr).u8[11]); \ + NRF_LOG_RAW_INFO("%02x%02x:%02x%02x\r\n",(addr).u8[12],(addr).u8[13],(addr).u8[14],(addr).u8[15]); + +/**< EUI 64 data type. */ +typedef struct +{ + uint8_t identifier[EUI_64_ADDR_SIZE]; /**< 64-bit identifier. */ +} eui64_t; + +/**< IPv6 address data type. */ +typedef union +{ + uint8_t u8[16]; + uint16_t u16[8]; + uint32_t u32[4]; +} ipv6_addr_t; + +extern ipv6_addr_t ipv6_addr_any; +#define IPV6_ADDR_ANY &ipv6_addr_any /**< IPV6 address represents any address. */ + +extern eui64_t eui64_local_iid; /**< External variable assumed to be implemented in the application with desired EUI-64 to be used as the IID for SLAAC. */ +#define EUI64_LOCAL_IID &eui64_local_iid /**< EUI-64 IID of the device. */ + +/** @brief IPv6 address states. */ +typedef enum +{ + IPV6_ADDR_STATE_UNUSED = 0, /**< IPv6 address is unused. */ + IPV6_ADDR_STATE_TENTATIVE, /**< IPv6 tentative address; DUD must be performed. */ + IPV6_ADDR_STATE_PREFERRED, /**< IPv6 preferred address; normal. state. */ + IPV6_ADDR_STATE_DEPRECATED /**< IPv6 deprecated address. */ +} ipv6_addr_state_t; + +/**< IPv6 header structure. */ +typedef struct +{ + uint8_t version_traffic_class; /**< Version and traffic class field. */ + uint8_t traffic_class_flowlabel; /**< Traffic class and flow label field. */ + uint16_t flowlabel; /**< Flow label, 2nd part of field. */ + uint16_t length; /**< Length of IPv6 payload field. */ + uint8_t next_header; /**< Next header field. */ + uint8_t hoplimit; /**< Hop limit field. */ + ipv6_addr_t srcaddr; /**< IPv6 source address field. */ + ipv6_addr_t destaddr; /**< IPv6 destination address field. */ +} ipv6_header_t; + +/**< IPv6 UDP header structure. */ +typedef struct +{ + uint16_t srcport; /**< Source port. */ + uint16_t destport; /**< Destination port. */ + uint16_t length; /**< Length of data with UDP header. */ + uint16_t checksum; /**< UDP checksum field. */ +} udp6_header_t; + +/**< IPv6 ICMP header structure. */ +typedef struct +{ + uint8_t type; /**< Type of ICMP message. See @ref icmp6_type for possible values. */ + uint8_t code; /**< Code related to the type field. */ + uint16_t checksum; /**< ICMP6 checksum field. */ + union /**< Message specific fields if any. */ + { + uint32_t mtu; /**< MTU of next hop limit. Used only with Packet Too Big Error Message. */ + uint32_t unused; /**< Unused fields for error messages that do not have any auxiliary information. */ + uint32_t offset; /**< Offset field used only with Parameter Problem error message. */ + struct { /**< Identifier and sequence number information specific associated with echo request and response. */ + uint16_t id; /**< Identifier. */ + uint16_t sequence; /**< Sequence number. */ + } echo; + } sp; +} icmp6_header_t; + +#endif // NRF52 + +#ifdef __cplusplus +} +#endif + +#endif //IOT_DEFINES_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/common/iot_errors.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/common/iot_errors.h new file mode 100644 index 0000000..657004a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/common/iot_errors.h @@ -0,0 +1,290 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * + * @defgroup iot_error IoT SDK error codes + * @{ + * @ingroup iot_sdk_common + * @{ + * @brief Error codes for the nRF5x IoT SDK. + * + * @details + * Error codes are 32-bit unsigned integers. The most significant 16 bits (MSB) are reserved for + * identifying the module where the error occurred. The least significant 16 bits (LSB) + * are used to provide the cause or nature of error. Each module is assigned a 16-bit + * unsigned integer, which it will use to identify all errors that occurred in the module. + * For example, if 0x8800 identifies a certain SDK module, all error codes from + * 0x88000000 to 0x8800FFFF are reserved for this module. + * + * Note that common error reasons have been assigned values to make it + * possible to decode error reasons easily. As an example, if a module is not initialized, + * this error is assigned the error code 0x000A0. If an application encounters an error code + * 0xZZZZ00A0, the application knows that it tried to access a certain module without + * initializing it. + * + * Each module can define error codes that are not covered by + * the common codes. These values must be defined in a range that does not conflict + * with the common error values. Such module-specific error values might be used by + * different modules to indicate errors of very different nature; so the same error code LSB + * does not necessarily indicate the same error. If an error is already defined by the NRF + * common error codes, however, these codes are reused. + * + * A specific range is also reserved for the application. The application can use this range + * to define application-specific errors. + * + * The success code NRF_SUCCESS does not include a module identifier. + + */ + +#ifndef IOT_ERRORS_H__ +#define IOT_ERRORS_H__ + +#include "sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup sdk_iot_err_base Base defined for IoT SDK Modules + * @{ + */ +#define IOT_ERR_BASE NRF_ERROR_IOT_ERR_BASE_START +/* @} */ + + +/** + * @defgroup sdk_iot_module_codes Module codes + * @brief Codes reserved as identification for the module where the error occurred. + * @{ + */ +#define BLE_6LOWPAN_ERR_BASE (IOT_ERR_BASE+0x0200) + +#define IOT_PBUFFER_ERR_BASE (IOT_ERR_BASE+0x1000) +#define IOT_CONTEXT_MANAGER_ERR_BASE (IOT_ERR_BASE+0x1100) +#define IOT_TIMER_ERR_BASE (IOT_ERR_BASE+0x1200) +#define IOT_FILE_ERR_BASE (IOT_ERR_BASE+0x1300) +#define IOT_DFU_ERR_BASE (IOT_ERR_BASE+0x1400) +#define IOT_IPV6_ERR_BASE (IOT_ERR_BASE+0x2000) +#define IOT_ICMP6_ERR_BASE (IOT_ERR_BASE+0x2100) +#define IOT_UDP6_ERR_BASE (IOT_ERR_BASE+0x2200) +#define IOT_COAP_ERR_BASE (IOT_ERR_BASE+0x2300) +#define IOT_DNS6_ERR_BASE (IOT_ERR_BASE+0x2400) +#define IOT_NTP_ERR_BASE (IOT_ERR_BASE+0x2500) +#define IOT_LWM2M_ERR_BASE (IOT_ERR_BASE+0x2600) +#define IOT_SOCKET_ERR_BASE (IOT_ERR_BASE+0x2700) +#define IOT_TFTP_ERR_BASE (IOT_ERR_BASE+0x2800) +#define IOT_MQTT_ERR_BASE (IOT_ERR_BASE+0x2900) +#define IOT_TLS_ERR_BASE (IOT_ERR_BASE+0x2A00) +#define IOT_TRICKLE_ERR_BASE (IOT_ERR_BASE+0x2C00) +/* @} */ + + +/** + * @defgroup iot_sdk_common_errors Common error codes + * @brief Codes reserved as identification for common errors. + * @{ + */ +#define SDK_ERR_MODULE_NOT_INITIALIZED (NRF_ERROR_SDK_COMMON_ERROR_BASE+0x0000) +#define SDK_ERR_MUTEX_INIT_FAILED (NRF_ERROR_SDK_COMMON_ERROR_BASE+0x0001) +#define SDK_ERR_MUTEX_LOCK_FAILED (NRF_ERROR_SDK_COMMON_ERROR_BASE+0x0002) +#define SDK_ERR_MUTEX_UNLOCK_FAILED (NRF_ERROR_SDK_COMMON_ERROR_BASE+0x0003) +#define SDK_ERR_MUTEX_COND_INIT_FAILED (NRF_ERROR_SDK_COMMON_ERROR_BASE+0x0004) +#define SDK_ERR_MODULE_ALREADY_INITIALZED (NRF_ERROR_SDK_COMMON_ERROR_BASE+0x0005) +#define SDK_ERR_API_NOT_IMPLEMENTED (NRF_ERROR_SDK_COMMON_ERROR_BASE+0x0010) +#define SDK_ERR_FEATURE_NOT_ENABLED (NRF_ERROR_SDK_COMMON_ERROR_BASE+0x0011) +#define SDK_ERR_RX_PKT_TRUNCATED (NRF_ERROR_SDK_COMMON_ERROR_BASE+0x0012) +/* @} */ + + +/** + * @defgroup ble_6lowpan_errors 6LoWPAN codes + * @brief Error and status codes specific to 6LoWPAN. + * @{ + */ +#define BLE_6LOWPAN_CID_NOT_FOUND (BLE_6LOWPAN_ERR_BASE+0x0040) +#define BLE_6LOWPAN_TX_FIFO_FULL (BLE_6LOWPAN_ERR_BASE+0x0041) +/* @} */ + + +/** + * @defgroup iot_sdk_ipv6 IPv6 codes + * @brief Error and status codes specific to IPv6. + * @{ + */ +#define IOT_IPV6_ERR_ADDR_IF_MISMATCH (IOT_IPV6_ERR_BASE+0x0040) +#define IOT_IPV6_ERR_PENDING (IOT_IPV6_ERR_BASE+0x0041) +/* @} */ + + +/** + * @defgroup iot_sdk_udp_errors UDP codes + * @brief Error and status codes specific to UDP. + * @{ + */ +#define UDP_PORT_IN_USE (IOT_UDP6_ERR_BASE+0x0040) +#define UDP_BAD_CHECKSUM (IOT_UDP6_ERR_BASE+0x0041) +#define UDP_TRUNCATED_PACKET (IOT_UDP6_ERR_BASE+0x0042) +#define UDP_MALFORMED_PACKET (IOT_UDP6_ERR_BASE+0x0043) +#define UDP_INTERFACE_NOT_READY (IOT_UDP6_ERR_BASE+0x0044) +/* @} */ + +/** + * @defgroup iot_sdk_socket_errors socket error codes + * @brief Error and status codes specific to socket API. + * @{ + */ +#define SOCKET_PORT_IN_USE (IOT_SOCKET_ERR_BASE + 0x0040) +#define SOCKET_NO_AVAILABLE_PORTS (IOT_SOCKET_ERR_BASE + 0x0041) +#define SOCKET_WOULD_BLOCK (IOT_SOCKET_ERR_BASE + 0x0042) +#define SOCKET_NO_MEM (IOT_SOCKET_ERR_BASE + 0x0043) +#define SOCKET_NO_ROUTE (IOT_SOCKET_ERR_BASE + 0x0044) +#define SOCKET_TIMEOUT (IOT_SOCKET_ERR_BASE + 0x0045) +#define SOCKET_INTERFACE_NOT_READY (IOT_SOCKET_ERR_BASE + 0x0046) +#define SOCKET_INVALID_PARAM (IOT_SOCKET_ERR_BASE + 0x0047) +#define SOCKET_UNSUPPORTED_PROTOCOL (IOT_SOCKET_ERR_BASE + 0x0048) +#define SOCKET_ADDRESS_IN_USE (IOT_SOCKET_ERR_BASE + 0x0049) +#define SOCKET_NOT_CONNECTED (IOT_SOCKET_ERR_BASE + 0x0050) +/* @} */ + +/** + * @defgroup iot_sdk_icmp6_errors ICMP codes + * @brief Error and status codes specific to ICMP. + * @{ + */ +#define ICMP6_UNHANDLED_PACKET_TYPE (IOT_ICMP6_ERR_BASE+0x0040) +#define ICMP6_BAD_CHECKSUM (IOT_ICMP6_ERR_BASE+0x0041) +#define ICMP6_MALFORMED_PACKET (IOT_ICMP6_ERR_BASE+0x0042) +#define ICMP6_INVALID_PACKET_DATA (IOT_ICMP6_ERR_BASE+0x0043) +/* @} */ + + +/** + * @defgroup iot_sdk_coap_errros CoAP codes + * @brief Error and status codes specific to CoAP. + * @{ + */ +#define COAP_MESSAGE_ERROR_NULL (IOT_COAP_ERR_BASE+0x0040) +#define COAP_MESSAGE_ERROR_INVALID_CONF (IOT_COAP_ERR_BASE+0x0041) +#define COAP_MESSAGE_INVALID_CONTENT (IOT_COAP_ERR_BASE+0x0042) +#define COAP_TRANSMISSION_RESET_BY_PEER (IOT_COAP_ERR_BASE+0x0043) +#define COAP_TRANSMISSION_TIMEOUT (IOT_COAP_ERR_BASE+0x0044) +#define COAP_TRANSPORT_SECURITY_MISSING (IOT_COAP_ERR_BASE+0x0045) +#define COAP_REQUEST_RESOURCE_NOT_FOUND (IOT_COAP_ERR_BASE+0x0046) +#define COAP_REQUEST_HANDLER_NOT_FOUND (IOT_COAP_ERR_BASE+0x0047) +/* @} */ + + +/** + * @defgroup iot_sdk_dns6_errors DNS codes. + * @brief Error and status codes specific to DNS. + * @{ + */ +#define DNS6_SERVER_UNREACHABLE (IOT_DNS6_ERR_BASE+0x0040) +#define DNS6_FORMAT_ERROR (IOT_DNS6_ERR_BASE+0x0041) +#define DNS6_SERVER_FAILURE (IOT_DNS6_ERR_BASE+0x0042) +#define DNS6_HOSTNAME_NOT_FOUND (IOT_DNS6_ERR_BASE+0x0043) +#define DNS6_NOT_IMPLEMENTED (IOT_DNS6_ERR_BASE+0x0044) +#define DNS6_REFUSED_ERROR (IOT_DNS6_ERR_BASE+0x0045) +#define DNS6_RESPONSE_TRUNCATED (IOT_DNS6_ERR_BASE+0x0046) +/* @} */ + + +/** + * @defgroup iot_sdk_ntp_errors NTP codes. + * @brief Error and status codes specific to NTP. + * @{ + */ +#define NTP_SERVER_UNREACHABLE (IOT_NTP_ERR_BASE+0x0040) +#define NTP_SERVER_BAD_RESPONSE (IOT_NTP_ERR_BASE+0x0041) +#define NTP_SERVER_KOD_PACKET_RECEIVED (IOT_NTP_ERR_BASE+0x0042) +/* @} */ + +/** + * @defgroup iot_sdk_tftp_errors TFTP codes. + * @brief Error and status codes specific to TFTP. + * @{ + */ + /**@brief TFTP Error codes. */ +#define TFTP_UNDEFINED_ERROR (IOT_TFTP_ERR_BASE+0x0040) +#define TFTP_FILE_NOT_FOUND (IOT_TFTP_ERR_BASE+0x0041) +#define TFTP_ACCESS_DENIED (IOT_TFTP_ERR_BASE+0x0042) +#define TFTP_FULL_STORAGE (IOT_TFTP_ERR_BASE+0x0043) +#define TFTP_INVALID_OPCODE (IOT_TFTP_ERR_BASE+0x0044) +#define TFTP_INVALID_TID (IOT_TFTP_ERR_BASE+0x0045) +#define TFTP_FILE_EXSISTS (IOT_TFTP_ERR_BASE+0x0046) +#define TFTP_WRONG_USERNAME (IOT_TFTP_ERR_BASE+0x0047) +#define TFTP_OPTION_REJECT (IOT_TFTP_ERR_BASE+0x0048) +#define TFTP_INVALID_PACKET (IOT_TFTP_ERR_BASE+0x0049) +#define TFTP_REMOTE_UNREACHABLE (IOT_TFTP_ERR_BASE+0x004A) + /* @} */ + +/**@defgroup iot_sdk_mqtt_err_code MQTT Error Codes + *@{ + */ +#define MQTT_ERR_NOT_CONNECTED (IOT_MQTT_ERR_BASE+0x0040) +#define MQTT_ERR_WRITE (IOT_MQTT_ERR_BASE+0x0041) +#define MQTT_ERR_TCP_PROC_FAILED (IOT_MQTT_ERR_BASE+0x0042) +#define MQTT_CONNECTION_FAILED (IOT_MQTT_ERR_BASE+0x0043) +#define MQTT_ERR_TRANSPORT_CLOSED (IOT_MQTT_ERR_BASE+0x0044) +/**@} + */ + + +/**@defgroup iot_sdk_tls_err_code NRF TLS Interface Error Codes + *@{ + */ +#define NRF_TLS_CONFIGURATION_FAILED (IOT_TLS_ERR_BASE+0x0040) +#define NRF_TLS_CONTEXT_SETUP_FAILED (IOT_TLS_ERR_BASE+0x0041) +#define NRF_TLS_HANDSHAKE_IN_PROGRESS (IOT_TLS_ERR_BASE+0x0042) +#define NRF_TLS_NO_FREE_INSTANCE (IOT_TLS_ERR_BASE+0x0043) +#define NRF_TLS_INVALID_CA_CERTIFICATE (IOT_TLS_ERR_BASE+0x0044) +#define NRF_TLS_OWN_CERT_SETUP_FAILED (IOT_TLS_ERR_BASE+0x0045) +/**@} + */ + +/** @} */ +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // IOT_ERRORS_H__ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/context_manager/iot_context_manager.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/context_manager/iot_context_manager.c new file mode 100644 index 0000000..a0c66ac --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/context_manager/iot_context_manager.c @@ -0,0 +1,561 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "nordic_common.h" +#include "sdk_common.h" +#include "sdk_config.h" +#include "iot_common.h" +#include "iot_context_manager.h" + +#if IOT_CONTEXT_MANAGER_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME context_manager + +#define NRF_LOG_LEVEL IOT_CONTEXT_MANAGER_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR IOT_CONTEXT_MANAGER_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR IOT_CONTEXT_MANAGER_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define CM_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define CM_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define CM_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define CM_ENTRY() CM_TRC(">> %s", __func__) +#define CM_EXIT() CM_TRC("<< %s", __func__) + +#else // IOT_CONTEXT_MANAGER_CONFIG_LOG_ENABLED + +#define CM_TRC(...) /**< Disables traces. */ +#define CM_DUMP(...) /**< Disables dumping of octet streams. */ +#define CM_ERR(...) /**< Disables error logs. */ + +#define CM_ENTRY(...) +#define CM_EXIT(...) + +#endif // IOT_CONTEXT_MANAGER_CONFIG_LOG_ENABLED + +/**@brief Parameter maximum values. */ +#define CID_VALUE_MAX 15 +#define PREFIX_LENGTH_VALUE_MAX 128 + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * IOT_CONTEXT_MANAGER_DISABLE_API_PARAM_CHECK should be defined to disable these checks. + * + * @{ + */ +#if (IOT_CONTEXT_MANAGER_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Macro to check is module is initialized before requesting one of the module procedures. */ +#define VERIFY_MODULE_IS_INITIALIZED() \ + if (m_initialization_state == false) \ + { \ + return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_CONTEXT_MANAGER_ERR_BASE); \ + } + +/**@brief Macro to check is module is initialized before requesting one of the module procedures. */ +#define VERIFY_MODULE_IS_INITIALIZED_NULL() \ + if (m_initialization_state == false) \ + { \ + return NULL; \ + } + +/** + * @brief Verify NULL parameters are not passed to API by application. + */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_CONTEXT_MANAGER_ERR_BASE); \ + } + +/** + * @brief Verify CID has valid value. + */ +#define VERIFY_CID_VALUE(CID) \ + if (!((CID) <= CID_VALUE_MAX)) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_CONTEXT_MANAGER_ERR_BASE); \ + } + +/** + * @brief Verify prefix length value. + */ +#define VERIFY_PREFIX_LEN_VALUE(PREFIX_LEN) \ + if (!(PREFIX_LEN <= PREFIX_LENGTH_VALUE_MAX)) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_CONTEXT_MANAGER_ERR_BASE); \ + } + +#else // IOT_IOT_CONTEXT_MANAGER_DISABLE_API_PARAM_CHECK + +#define VERIFY_MODULE_IS_INITIALIZED() +#define VERIFY_MODULE_IS_INITIALIZED_NULL() +#define NULL_PARAM_CHECK(PARAM) +#define VERIFY_CID_VALUE(CID) +#define VERIFY_PREFIX_LEN_VALUE(PREFIX_LEN) + +#endif //IOT_CONTEXT_MANAGER_DISABLE_API_PARAM_CHECK +/** @} */ + +/** + * @defgroup iot_context_manager_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case need arises to use an alternative architecture. + * @{ + */ +#define CM_MUTEX_LOCK() SDK_MUTEX_LOCK(m_iot_context_manager_mutex) /**< Lock module using mutex */ +#define CM_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_iot_context_manager_mutex) /**< Unlock module using mutex */ +/** @} */ + +/**@brief Context table, managing by IPv6 stack. */ +typedef struct +{ + iot_interface_t * p_interface; /**< IoT interface pointer. */ + uint8_t context_count; /**< Number of valid contexts in the table. */ + iot_context_t contexts[IOT_CONTEXT_MANAGER_MAX_CONTEXTS]; /**< Array of valid contexts. */ +}iot_context_table_t; + + +SDK_MUTEX_DEFINE(m_iot_context_manager_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ +static bool m_initialization_state = false; /**< Variable to maintain module initialization state. */ +static iot_context_table_t m_context_table[IOT_CONTEXT_MANAGER_MAX_TABLES]; /**< Array of contexts table managed by the module. */ + +/**@brief Initializes context entry. */ +static void context_init(iot_context_t * p_context) +{ + p_context->context_id = IPV6_CONTEXT_IDENTIFIER_NONE; + p_context->prefix_len = 0; + p_context->compression_flag = false; + + memset(p_context->prefix.u8, 0, IPV6_ADDR_SIZE); +} + + +/**@brief Initializes context table. */ +static void context_table_init(uint32_t table_id) +{ + uint32_t index; + + for (index = 0; index < IOT_CONTEXT_MANAGER_MAX_CONTEXTS; index++) + { + context_init(&m_context_table[table_id].contexts[index]); + m_context_table[table_id].context_count = 0; + m_context_table[table_id].p_interface = NULL; + } +} + + +/**@brief Initializes context table. */ +static uint32_t context_table_find(const iot_interface_t * p_interface) +{ + uint32_t index; + + for (index = 0; index < IOT_CONTEXT_MANAGER_MAX_TABLES; index++) + { + if (m_context_table[index].p_interface == p_interface) + { + break; + } + } + + return index; +} + + +/**@brief Looks up context table for specific context identifier. */ +static uint32_t context_find_by_cid(uint32_t table_id, + uint8_t context_id, + iot_context_t ** pp_context) +{ + uint32_t index; + + for (index = 0; index < IOT_CONTEXT_MANAGER_MAX_CONTEXTS; index++) + { + if (m_context_table[table_id].contexts[index].context_id == context_id) + { + *pp_context = &m_context_table[table_id].contexts[index]; + return NRF_SUCCESS; + } + } + + return (NRF_ERROR_NOT_FOUND | IOT_CONTEXT_MANAGER_ERR_BASE); +} + + +static uint32_t context_find_by_prefix(uint32_t table_id, + const ipv6_addr_t * p_prefix, + iot_context_t ** pp_context) +{ + uint32_t index; + uint32_t context_left; + uint16_t context_cmp_len; + iot_context_t * p_best_match = NULL; + + context_left = m_context_table[table_id].context_count; + + for (index = 0; index < IOT_CONTEXT_MANAGER_MAX_CONTEXTS && context_left; index++) + { + if (m_context_table[table_id].contexts[index].context_id != IPV6_CONTEXT_IDENTIFIER_NONE && + m_context_table[table_id].contexts[index].compression_flag == true) + { + if ((context_cmp_len = m_context_table[table_id].contexts[index].prefix_len) < 64) + { + context_cmp_len = 64; + } + + // Check if address have matched in CID table. + if (IPV6_ADDRESS_PREFIX_CMP(m_context_table[table_id].contexts[index].prefix.u8, + p_prefix->u8, + context_cmp_len)) + { + if (p_best_match == NULL || p_best_match->prefix_len < + m_context_table[table_id].contexts[index].prefix_len) + { + p_best_match = &m_context_table[table_id].contexts[index]; + } + } + + // Decrease left context in table, to avoid too many checking. + context_left--; + } + } + + if (p_best_match) + { + *pp_context = p_best_match; + return NRF_SUCCESS; + } + + return (NRF_ERROR_NOT_FOUND | IOT_CONTEXT_MANAGER_ERR_BASE); +} + + +/**@brief Looks up for first empty entry in the table. */ +static uint32_t context_find_free(uint32_t table_id, iot_context_t ** pp_context) +{ + uint32_t index; + + for (index = 0; index < IOT_CONTEXT_MANAGER_MAX_CONTEXTS; index++) + { + if (m_context_table[table_id].contexts[index].context_id == IPV6_CONTEXT_IDENTIFIER_NONE) + { + *pp_context = &m_context_table[table_id].contexts[index]; + return NRF_SUCCESS; + } + } + + return (NRF_ERROR_NO_MEM | IOT_CONTEXT_MANAGER_ERR_BASE); +} + + +uint32_t iot_context_manager_init(void) +{ + uint32_t index; + + CM_ENTRY(); + + SDK_MUTEX_INIT(m_iot_context_manager_mutex); + + CM_MUTEX_LOCK(); + + for (index = 0; index < IOT_CONTEXT_MANAGER_MAX_TABLES; index++) + { + context_table_init(index); + } + + m_initialization_state = true; + + CM_MUTEX_UNLOCK(); + + CM_EXIT(); + + return NRF_SUCCESS; +} + + +uint32_t iot_context_manager_table_alloc(const iot_interface_t * p_interface) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + + uint32_t err_code = NRF_SUCCESS; + + CM_ENTRY(); + + CM_MUTEX_LOCK(); + + const uint32_t table_id = context_table_find(NULL); + + if (table_id != IOT_CONTEXT_MANAGER_MAX_TABLES) + { + // Found a free context table and assign to it. + CM_TRC("Assigned new context table."); + m_context_table[table_id].p_interface = (iot_interface_t *)p_interface; + } + else + { + // No free context table found. + CM_ERR("No context table found."); + err_code = (NRF_ERROR_NO_MEM | IOT_CONTEXT_MANAGER_ERR_BASE); + } + + CM_MUTEX_UNLOCK(); + + CM_EXIT(); + + return err_code; +} + + +uint32_t iot_context_manager_table_free(const iot_interface_t * p_interface) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + + uint32_t err_code = NRF_SUCCESS; + + CM_ENTRY(); + + SDK_MUTEX_INIT(m_iot_context_manager_mutex); + + CM_MUTEX_LOCK(); + + const uint32_t table_id = context_table_find(p_interface); + + if (table_id != IOT_CONTEXT_MANAGER_MAX_TABLES) + { + // Clear context table. + CM_TRC("Found context table assigned to interface."); + context_table_init(table_id); + } + else + { + // No free context table found. + CM_ERR("No context table found."); + err_code = (NRF_ERROR_NOT_FOUND | IOT_CONTEXT_MANAGER_ERR_BASE); + } + + CM_MUTEX_UNLOCK(); + + CM_EXIT(); + + return err_code; +} + + +uint32_t iot_context_manager_update(const iot_interface_t * p_interface, + iot_context_t * p_context) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_context); + VERIFY_CID_VALUE(p_context->context_id); + VERIFY_PREFIX_LEN_VALUE(p_context->prefix_len); + + uint32_t retval = NRF_SUCCESS; + uint32_t err_code = NRF_SUCCESS; + iot_context_t * p_internal_context; + + CM_ENTRY(); + + CM_MUTEX_LOCK(); + + const uint32_t table_id = context_table_find(p_interface); + + if (table_id != IOT_CONTEXT_MANAGER_MAX_TABLES) + { + // Try to find context in context table. + retval = context_find_by_cid(table_id, p_context->context_id, &p_internal_context); + + if (retval != NRF_SUCCESS) + { + err_code = context_find_free(table_id, &p_internal_context); + + // Increase context count. + if (err_code == NRF_SUCCESS) + { + m_context_table[table_id].context_count++; + } + } + + if (err_code == NRF_SUCCESS) + { + // Update context table, with parameters from application. + p_internal_context->context_id = p_context->context_id; + p_internal_context->prefix_len = p_context->prefix_len; + p_internal_context->compression_flag = p_context->compression_flag; + memset(p_internal_context->prefix.u8, 0, IPV6_ADDR_SIZE); + IPV6_ADDRESS_PREFIX_SET(p_internal_context->prefix.u8, p_context->prefix.u8, p_context->prefix_len); + } + else + { + CM_ERR("No place in context table."); + } + } + else + { + // No free context table found. + CM_ERR("No context table found."); + err_code = (NRF_ERROR_NOT_FOUND | IOT_CONTEXT_MANAGER_ERR_BASE); + } + + CM_MUTEX_UNLOCK(); + + CM_EXIT(); + + return err_code; +} + + +uint32_t iot_context_manager_remove(const iot_interface_t * p_interface, + iot_context_t * p_context) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_context); + + uint32_t err_code = NRF_SUCCESS; + + CM_ENTRY(); + + CM_MUTEX_LOCK(); + + const uint32_t table_id = context_table_find(p_interface); + + if (table_id != IOT_CONTEXT_MANAGER_MAX_TABLES) + { + if (p_context->context_id != IPV6_CONTEXT_IDENTIFIER_NONE) + { + m_context_table[table_id].context_count--; + } + + // Reinit context entry. + context_init(p_context); + } + else + { + // No free context table found. + CM_ERR("No context table found."); + err_code = (NRF_ERROR_NOT_FOUND | IOT_CONTEXT_MANAGER_ERR_BASE); + } + + CM_MUTEX_UNLOCK(); + + CM_EXIT(); + + return err_code; +} + + +uint32_t iot_context_manager_get_by_addr(const iot_interface_t * p_interface, + const ipv6_addr_t * p_addr, + iot_context_t ** pp_context) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_addr); + NULL_PARAM_CHECK(pp_context); + + uint32_t err_code; + + CM_ENTRY(); + + CM_MUTEX_LOCK(); + + const uint32_t table_id = context_table_find(p_interface); + + if (table_id != IOT_CONTEXT_MANAGER_MAX_TABLES) + { + err_code = context_find_by_prefix(table_id, p_addr, pp_context); + } + else + { + // No free context table found. + CM_ERR("No context table found."); + err_code = (NRF_ERROR_NOT_FOUND | IOT_CONTEXT_MANAGER_ERR_BASE); + } + + CM_MUTEX_UNLOCK(); + + CM_EXIT(); + + return err_code; +} + + +uint32_t iot_context_manager_get_by_cid(const iot_interface_t * p_interface, + uint8_t context_id, + iot_context_t ** pp_context) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(pp_context); + VERIFY_CID_VALUE(context_id); + + uint32_t err_code; + + CM_ENTRY(); + + CM_MUTEX_LOCK(); + + const uint32_t table_id = context_table_find(p_interface); + + if (table_id != IOT_CONTEXT_MANAGER_MAX_TABLES) + { + err_code = context_find_by_cid(table_id, context_id, pp_context); + } + else + { + // No free context table found. + CM_TRC("No context table found."); + err_code = (NRF_ERROR_NOT_FOUND | IOT_CONTEXT_MANAGER_ERR_BASE); + } + + CM_MUTEX_UNLOCK(); + + CM_EXIT(); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/context_manager/iot_context_manager.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/context_manager/iot_context_manager.h new file mode 100644 index 0000000..7fec245 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/context_manager/iot_context_manager.h @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup iot_context_manager Context Manager + * @{ + * @ingroup iot_sdk_common + * @brief Manages context identifiers and prefixes related to the identifiers. + * + * @details This module allows to handle context information throughout the IoT application. + * The module is used in the compression and decompression procedures of IPv6 addresses. + * It allows more efficient communication between two nodes using global addresses. + * The Context Manager contains tables of context, which can be accessed through API functions. + * The table is maintained by the IPv6 stack while is referenced by 6LoWPAN module to be able to + * compress and decompress packets. + * + * You can configure the module by changing the @c sdk_config.h configuration file. + */ + +#ifndef IOT_CONTEXT_MANAGER__ +#define IOT_CONTEXT_MANAGER__ + +#include <stdint.h> +#include <stdbool.h> +#include "iot_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Function for initializing the module. + * + * @retval NRF_SUCCESS If the module was successfully initialized. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t iot_context_manager_init(void); + + +/**@brief Function for allocating the table for the specific interface. + * + * @param[in] p_interface Pointer to the IoT interface. + * + * @retval NRF_SUCCESS If the table was successfully allocated. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t iot_context_manager_table_alloc(const iot_interface_t * p_interface); + + +/**@brief Function for freeing the table for the specific interface. + * + * @param[in] p_interface Pointer to the IoT interface. + * + * @retval NRF_SUCCESS If the table was successfully freed. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t iot_context_manager_table_free(const iot_interface_t * p_interface); + + +/**@brief Function for updating the context table. + * + * Update requests are treated as follows: + * - If the context identifier already exists in the context table, the context + * is updated. + * - If the context identifier does not exist yet, a new entry is generated. If + * no memory is available, NRF_ERROR_NO_MEMORY is returned. + * + * The compression flag indicates if a context can be used for compression. + * The context table can hold up to 16 context's information. + * + * @param[in] p_interface Pointer to the IoT interface. + * @param[in] p_context Pointer to the context entry that shall be modified or added. + * + * @retval NRF_SUCCESS If the table was successfully updated. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t iot_context_manager_update(const iot_interface_t * p_interface, iot_context_t * p_context); + + +/**@brief Function for removing context from the context table. + * + * @param[in] p_interface Pointer to the IoT interface. + * @param[in] p_context Pointer to the context entry, retrieved from @ref iot_context_manager_get_by_addr or + * @ref iot_context_manager_get_by_cid. + * + * @retval NRF_SUCCESS If the context was successfully removed. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t iot_context_manager_remove(const iot_interface_t * p_interface, iot_context_t * p_context); + + +/**@brief Function for searching the proper entry in the context table by IPv6 address. + * Only contexts with compression flag set to true, may be returned. + * + * @param[in] p_interface Pointer to the IoT interface. + * @param[in] p_addr Pointer to IPv6 address to be compared with records in the context table. + * @param[out] pp_context Pointer to the context in the context table. + * + * @retval NRF_SUCCESS If the procedure succeeded. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t iot_context_manager_get_by_addr(const iot_interface_t * p_interface, + const ipv6_addr_t * p_addr, + iot_context_t ** pp_context); + + +/**@brief Function for searching the proper entry in the context table by context identifier. + * + * @param[in] p_interface Pointer to the IoT interface. + * @param[in] context_id Context identifier to be compared with records in the context table. + * @param[out] pp_context Pointer to the context in the context table. + * + * @retval NRF_SUCCESS If the procedure succeeded. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t iot_context_manager_get_by_cid(const iot_interface_t * p_interface, + uint8_t context_id, + iot_context_t ** pp_context); + +#ifdef __cplusplus +} +#endif + +#endif // IOT_CONTEXT_MANAGER__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/errno/errno.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/errno/errno.c new file mode 100644 index 0000000..f1d79b5 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/errno/errno.c @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "errno.h" + +// TODO: Support multiple contexts +static int m_errno_main; + +int * +__error(void) +{ + return &m_errno_main; +} + +void +set_errno(int err_code) +{ + m_errno_main = err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/errno/errno.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/errno/errno.h new file mode 100644 index 0000000..f2ec459 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/errno/errno.h @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef ERRNO_H__ +#define ERRNO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define EBADF 9 +#define ENOMEM 12 +#define EFAULT 14 +#define EINVAL 22 +#define EMFILE 24 +#define EAGAIN 35 +#define EPROTONOSUPPORT 43 +#define EOPNOTSUPP 45 +#define EAFNOSUPPORT 47 +#define EADDRINUSE 48 +#define ENETDOWN 50 +#define ENETUNREACH 51 +#define ECONNRESET 54 +#define EISCONN 56 +#define ENOTCONN 57 +#define ETIMEDOUT 60 + +#define EINPROGRESS 115 /* Operation in progress. */ +#define ECANCELED 125 /* Operation canceled. */ +int * __error(void); +void set_errno(int); + +#undef errno +#define errno (* __error()) + +#ifdef __cplusplus +} +#endif + +#endif // ERRNO_H__ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/iot_file.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/iot_file.c new file mode 100644 index 0000000..285be29 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/iot_file.c @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_config.h" +#include "iot_file.h" +#include "iot_common.h" + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * IOT_FILE_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks. + * + * @{ + */ +#if (IOT_FILE_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_FILE_ERR_BASE); \ + } + +#else // IOT_FILE_DISABLE_API_PARAM_CHECK + +#define NULL_PARAM_CHECK(PARAM) + +#endif // IOT_FILE_DISABLE_API_PARAM_CHECK +/** @} */ + + +/** + * @brief Function to open IoT file. Depending on port, it should allocate required buffer. + */ +uint32_t iot_file_fopen(iot_file_t * p_file, uint32_t requested_size) +{ + NULL_PARAM_CHECK(p_file); + + if (p_file->open != NULL) + { + return p_file->open(p_file, requested_size); + } + else + { + p_file->buffer_size = requested_size; + return NRF_ERROR_API_NOT_IMPLEMENTED; + } +} + + +/** + * @brief Function to write data buffer into a file. + */ +uint32_t iot_file_fwrite(iot_file_t * p_file, const void * p_data, uint32_t size) +{ + NULL_PARAM_CHECK(p_file); + + if (p_file->write != NULL) + { + return p_file->write(p_file, p_data, size); + } + else + { + return NRF_ERROR_API_NOT_IMPLEMENTED; + } +} + + +/** + * @brief Function to read data buffer from file. + */ +uint32_t iot_file_fread(iot_file_t * p_file, void * p_data, uint32_t size) +{ + NULL_PARAM_CHECK(p_file); + + if (p_file->read != NULL) + { + return p_file->read(p_file, p_data, size); + } + else + { + return NRF_ERROR_API_NOT_IMPLEMENTED; + } +} + + +/** + * @brief Function to read current cursor position. + */ +uint32_t iot_file_ftell(iot_file_t * p_file, uint32_t * p_cursor) +{ + NULL_PARAM_CHECK(p_file); + + if (p_file->tell != NULL) + { + return p_file->tell(p_file, p_cursor); + } + else + { + return NRF_ERROR_API_NOT_IMPLEMENTED; + } +} + + +/** + * @brief Function to set current cursor position. + */ +uint32_t iot_file_fseek(iot_file_t * p_file, uint32_t cursor) +{ + NULL_PARAM_CHECK(p_file); + + if (p_file->seek != NULL) + { + return p_file->seek(p_file, cursor); + } + else + { + return NRF_ERROR_API_NOT_IMPLEMENTED; + } +} + + +/** + * @brief Function to rewind file cursor. + */ +uint32_t iot_file_frewind(iot_file_t * p_file) +{ + NULL_PARAM_CHECK(p_file); + + if (p_file->rewind != NULL) + { + return p_file->rewind(p_file); + } + else + { + return NRF_ERROR_API_NOT_IMPLEMENTED; + } +} + + +/** + * @brief Function to close IoT file. Depending on port, it should free used buffer. + */ +uint32_t iot_file_fclose(iot_file_t * p_file) +{ + NULL_PARAM_CHECK(p_file); + + if (p_file->close != NULL) + { + return p_file->close(p_file); + } + else + { + return NRF_ERROR_API_NOT_IMPLEMENTED; + } +} + diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/iot_file.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/iot_file.h new file mode 100644 index 0000000..c4afa7b --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/iot_file.h @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** + * @file iot_file.h + * @brief IoT File abstraction API. + */ + +#ifndef IOT_FILE_H__ +#define IOT_FILE_H__ + +#include "iot_common.h" +#include "iot_file_port.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup iot_sdk_common + * @addtogroup iot_file IoT File + * @brief IoT File abstraction definition. + * @{ + * @defgroup iot_file_abstraction IoT file abstraction + * @{ + * @brief Structures and public API definition. + */ + +/** + * @enum iot_file_evt_t + * @brief List of events returned in callback for file ports with asynchronous operation + * like open, read, write and close. + */ +typedef enum { + IOT_FILE_OPENED, /**< Event indicates that file has been opened.*/ + IOT_FILE_WRITE_COMPLETE, /**< Event indicates that single write operation has been completed.*/ + IOT_FILE_READ_COMPLETE, /**< Event indicates that single read operation has been completed.*/ + IOT_FILE_CLOSED, /**< Event indicates that file has been closed.*/ + IOT_FILE_ERROR /**< Event indicates that file encountered a problem.*/ +} iot_file_evt_t; + +/** + * @brief IoT File user callback type definition. + * + * @param[in] p_file Reference to an IoT file instance. + * @param[in] event Event structure describing what has happened. + * @param[in] result Result code (should be NRF_SUCCESS for all events except errors). + * @param[in] p_data Pointer to memory buffer. + * @param[in] size Size of data stored in memory buffer. + * + * @retval None. + */ +typedef void (*iot_file_callback_t) (iot_file_t * p_file, iot_file_evt_t event, uint32_t result, void * p_data, uint32_t size); + +/** + * @brief Function to open IoT file. Depending on port, it should allocate required buffer. + * + * @param[in] p_file Pointer to an IoT file instance. + * @param[in] requested_size Maximum number of bytes to be read/written. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_file_fopen(iot_file_t * p_file, uint32_t requested_size); + +/** + * @brief Function to write data buffer into a file. + * + * @param[in] p_file Pointer to an IoT file instance. + * @param[in] p_data Pointer to data block which will be written into the file. + * @param[in] size Number of bytes to be written. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_file_fwrite(iot_file_t * p_file, const void * p_data, uint32_t size); + +/** + * @brief Function to read data buffer from file. + * + * @param[in] p_file Pointer to an IoT file instance. + * @param[in] p_data Pointer to data block to be filled with read data. + * @param[in] size Number of bytes to be read. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_file_fread(iot_file_t * p_file, void * p_data, uint32_t size); + +/** + * @brief Function to read current cursor position. + * + * @param[in] p_file Pointer to an IoT file instance. + * @param[out] p_cursor Current value of a cursor. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_file_ftell(iot_file_t * p_file, uint32_t * p_cursor); + +/** + * @brief Function to set current cursor position. + * + * @param[in] p_file Pointer to an IoT file instance. + * @param[in] cursor New cursor value. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_file_fseek(iot_file_t * p_file, uint32_t cursor); + +/** + * @brief Function to rewind file cursor. + * + * @param[in] p_file Pointer to an IoT file instance. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_file_frewind(iot_file_t * p_file); + +/** + * @brief Function to close IoT file. Depending on port, it should free used buffer. + * + * @param[in] p_file Pointer to an IoT file instance. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_file_fclose(iot_file_t * p_file); + +#ifdef __cplusplus +} +#endif + +#endif // IOT_FILE_H__ + +/** @} */ +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/iot_file_port.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/iot_file_port.h new file mode 100644 index 0000000..23e53d3 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/iot_file_port.h @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** + * @file iot_file_port.h + * @brief Types and definitions used while writing port. + */ + +#ifndef IOT_FILE_PORT_H__ +#define IOT_FILE_PORT_H__ + +/** + * @defgroup iot_file_port_defs IoT file definition for port libraries. + * @{ + * @ingroup iot_file + * @brief Type definitions for port modules. + */ +#include <stdint.h> +#include <string.h> +#include "nordic_common.h" +#include "sdk_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//NOTE: Port functions don't need to check if p_file exists, because call can be done only on a correct file instance. + +#define IOT_FILE_INVALID_CURSOR 0xFFFFFFFF + +/** + * @enum iot_file_type_t + * @brief Supported file type values. + */ +typedef enum +{ + IOT_FILE_TYPE_UNKNOWN = 0, /**< File type used for describing not initialized files. */ + IOT_FILE_TYPE_PSTORAGE_RAW, /**< File type used for accessing flash memory. */ + IOT_FILE_TYPE_STATIC /**< File type used for representing static buffers. */ +} iot_file_type_t; + +/** + * @brief iot_file_t structure forward definition. + */ +typedef struct iot_file_struct_t iot_file_t; + +/** + * @brief IoT File fopen() callback type definition. + */ +typedef uint32_t (*iot_fopen_t)(iot_file_t * p_file, uint32_t requested_size); + +/** + * @brief IoT File fwrite() callback type definition. + */ +typedef uint32_t (*iot_fwrite_t)(iot_file_t * p_file, const void * p_data, uint32_t size); + +/** + * @brief IoT File fread() callback type definition. + */ +typedef uint32_t (*iot_fread_t)(iot_file_t * p_file, void * p_data, uint32_t size); + +/** + * @brief IoT File ftell() callback type definition. + */ +typedef uint32_t (*iot_ftell_t)(iot_file_t * p_file, uint32_t * p_cursor); + +/** + * @brief IoT File fseek() callback type definition. + */ +typedef uint32_t (*iot_fseek_t)(iot_file_t * p_file, uint32_t cursor); + +/** + * @brief IoT File frewind() callback type definition. + */ +typedef uint32_t (*iot_frewind_t)(iot_file_t * p_file); + +/** + * @brief IoT File fclose() callback type definition. + */ +typedef uint32_t (*iot_fclose_t)(iot_file_t * p_file); + +/** + * @brief Generic IoT File instance structure. + */ +struct iot_file_struct_t +{ + const char * p_filename; /**< Public. String constant describing file name. */ + iot_file_type_t type; /**< Public. Type of file. Each type should be added into iot_file_type_t enum. */ + uint32_t file_size; /**< Public. Number of valid bytes inside file. */ + + uint32_t cursor; /**< Internal. Cursor describing which byte will be read/written. */ + uint32_t buffer_size; /**< Internal. Size of data buffer. */ + void * p_buffer; /**< Internal. Pointer to a data buffer set by calling fopen. */ + void * p_callback; /**< Internal. User callback used in order to notify about finished file operations. */ + void * p_arg; /**< Internal. User defined argument, used inside the port. */ + iot_fopen_t open; /**< Internal. Callback for fopen operation assigned by particular port. */ + iot_fwrite_t write; /**< Internal. Callback for fwrite operation assigned by particular port. */ + iot_fread_t read; /**< Internal. Callback for fread operation assigned by particular port. */ + iot_ftell_t tell; /**< Internal. Callback for ftell operation assigned by particular port. */ + iot_fseek_t seek; /**< Internal. Callback for fseek operation assigned by particular port. */ + iot_frewind_t rewind; /**< Internal. Callback for frewind operation assigned by particular port. */ + iot_fclose_t close; /**< Internal. Callback for fclose operation assigned by particular port. */ +}; + +#ifdef __cplusplus +} +#endif + +#endif // IOT_FILE_PORT_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/static/iot_file_static.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/static/iot_file_static.c new file mode 100644 index 0000000..4712992 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/static/iot_file_static.c @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_config.h" +#include "iot_file_static.h" +#include "iot_common.h" +#include <string.h> + +#if IOT_FILE_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME iot_file_static + +#define NRF_LOG_LEVEL IOT_FILE_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR IOT_FILE_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR IOT_FILE_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define FSTATIC_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define FSTATIC_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define FSTATIC_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#else // IOT_FILE_CONFIG_LOG_ENABLED + +#define FSTATIC_TRC(...) /**< Disables traces. */ +#define FSTATIC_DUMP(...) /**< Disables dumping of octet streams. */ +#define FSTATIC_ERR(...) /**< Disables error logs. */ + +#endif // IOT_FILE_CONFIG_LOG_ENABLED + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * IOT_FILE_STATIC_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks. + * + * @{ + */ + +#if (IOT_FILE_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_FILE_ERR_BASE); \ + } + +#else // IOT_FILE_DISABLE_API_PARAM_CHECK + +#define NULL_PARAM_CHECK(PARAM) + +#endif // IOT_FILE_DISABLE_API_PARAM_CHECK +/** @} */ + +#define CHECK_CURSOR(PARAM) \ + if ((PARAM) == IOT_FILE_INVALID_CURSOR) \ + { \ + return (NRF_ERROR_INVALID_STATE | IOT_FILE_ERR_BASE); \ + } + +/**@brief Static buffer fwrite port function definition. */ +static uint32_t internal_fwrite(iot_file_t * p_file, const void * p_data, uint32_t size) +{ + NULL_PARAM_CHECK(p_data); + CHECK_CURSOR(p_file->cursor); + + if ((size + p_file->cursor) > p_file->buffer_size) + { + return (NRF_ERROR_DATA_SIZE | IOT_FILE_ERR_BASE); + } + + memcpy(((uint8_t *)p_file->p_buffer + p_file->cursor), p_data, size); + p_file->cursor += size; + + if (p_file->cursor > p_file->file_size) + { + p_file->file_size = p_file->cursor; + } + + return NRF_SUCCESS; +} + + +/**@brief Static buffer fread port function definition. */ +static uint32_t internal_fread(iot_file_t * p_file, void * p_data, uint32_t size) +{ + NULL_PARAM_CHECK(p_data); + CHECK_CURSOR(p_file->cursor); + + if (size + p_file->cursor > p_file->file_size) + { + return (NRF_ERROR_DATA_SIZE | IOT_FILE_ERR_BASE); + } + + memcpy(p_data, (uint8_t *)p_file->p_buffer + p_file->cursor, size); + p_file->cursor += size; + + return NRF_SUCCESS; +} + + +/**@brief Static ftell port function definition. */ +static uint32_t internal_ftell(iot_file_t * p_file, uint32_t * p_cursor) +{ + NULL_PARAM_CHECK(p_cursor); + CHECK_CURSOR(p_file->cursor); + + *p_cursor = p_file->cursor; + + return NRF_SUCCESS; +} + + +/**@brief Static fseek port function definition. */ +static uint32_t internal_fseek(iot_file_t * p_file, uint32_t cursor) +{ + CHECK_CURSOR(p_file->cursor); + + if (cursor > p_file->buffer_size) + { + FSTATIC_ERR("Cursor outside file!"); + return (NRF_ERROR_INVALID_PARAM | IOT_FILE_ERR_BASE); + } + + p_file->cursor = cursor; + + return NRF_SUCCESS; +} + + +/**@brief Static frewind port function definition. */ +static uint32_t internal_frewind(iot_file_t * p_file) +{ + CHECK_CURSOR(p_file->cursor); + + p_file->cursor = 0; + + return NRF_SUCCESS; +} + + +/**@brief Static fopen port function definition. */ +static uint32_t internal_fopen(iot_file_t * p_file, uint32_t requested_size) +{ + p_file->cursor = 0; + + if (requested_size != 0) + { + p_file->file_size = requested_size; + } + + return NRF_SUCCESS; +} + + +/**@brief Static fclose port function definition. */ +static uint32_t internal_fclose(iot_file_t * p_file) +{ + p_file->cursor = IOT_FILE_INVALID_CURSOR; + + return NRF_SUCCESS; +} + + +/**@brief This function is used to assign correct callbacks and file type to passed IoT File instance. */ +void iot_file_static_assign(iot_file_t * p_file) +{ + if (p_file == NULL) + { + return; + } + + p_file->type = IOT_FILE_TYPE_STATIC; + p_file->write = internal_fwrite; + p_file->read = internal_fread; + p_file->tell = internal_ftell; + p_file->seek = internal_fseek; + p_file->rewind = internal_frewind; + p_file->open = internal_fopen; + p_file->close = internal_fclose; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/static/iot_file_static.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/static/iot_file_static.h new file mode 100644 index 0000000..de3ea02 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_file/static/iot_file_static.h @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** + * @file iot_file_static.h + * @brief FILE port for static buffers. + */ + +#ifndef IOT_FILE_STATIC_H__ +#define IOT_FILE_STATIC_H__ + +#include "iot_file_port.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup iot_file_static IoT file port for static buffers + * @ingroup iot_file + * @{ + * @brief Macro function which simplifies file setup process and file type assigning function. + */ + +/** + * @brief This macro function configures passed file as a static buffer file. + */ +#define IOT_FILE_STATIC_INIT(p_iot_file, p_file_name, p_mem, size) \ + do { \ + (p_iot_file)->p_filename = p_file_name; \ + (p_iot_file)->cursor = IOT_FILE_INVALID_CURSOR; \ + (p_iot_file)->p_buffer = p_mem; \ + (p_iot_file)->buffer_size = size; \ + (p_iot_file)->file_size = 0; \ + (p_iot_file)->p_callback = NULL; \ + iot_file_static_assign(p_iot_file); \ + } while (0) + +/** + * @brief This function is used to assign correct callbacks and file type to passed IoT File instance. + */ +void iot_file_static_assign(iot_file_t * p_file); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // IOT_FILE_STATIC_H__ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_timer/iot_timer.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_timer/iot_timer.c new file mode 100644 index 0000000..96af2a6 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_timer/iot_timer.c @@ -0,0 +1,183 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <string.h> +#include "iot_timer.h" +#include "sdk_common.h" +#include "sdk_config.h" +#include "iot_errors.h" + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * IOT_TIMER_DISABLE_API_PARAM_CHECK should be defined to disable these checks. + * + * @{ + */ +#if (IOT_TIMER_DISABLE_API_PARAM_CHECK == 0) + +/** + * @brief Verify NULL parameters are not passed to API by application. + */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_TIMER_ERR_BASE); \ + } + +#define VERIFY_CLIENT_LIST_IS_VALID(PARAM) \ + if ((PARAM) != NULL) \ + { \ + uint8_t i; \ + for (i = 0; i < (PARAM)->client_list_length; i++) \ + { \ + if (((PARAM)->p_client_list[i].iot_timer_callback) == NULL) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_TIMER_ERR_BASE); \ + } \ + if (((PARAM)->p_client_list[i].cb_interval == 0) || \ + ((PARAM)->p_client_list[i].cb_interval < IOT_TIMER_RESOLUTION_IN_MS) || \ + (((PARAM)->p_client_list[i].cb_interval % IOT_TIMER_RESOLUTION_IN_MS) != 0)) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_TIMER_ERR_BASE); \ + } \ + } \ + } + +#define VERIFY_WALL_CLOCK_VALUE_IS_VALID(PARAM) \ + if ((PARAM) != NULL) \ + { \ + if ((*PARAM % IOT_TIMER_RESOLUTION_IN_MS) != 0) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_TIMER_ERR_BASE); \ + } \ + } + +/** + * @brief Verify NULL parameters are not passed to API by application. + */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_TIMER_ERR_BASE); \ + } + +#else // IOT_TIMER_DISABLE_API_PARAM_CHECK + +#define NULL_PARAM_CHECK(PARAM) +#define VERIFY_CLIENT_LIST_IS_VALID(PARAM) +#define VERIFY_WALL_CLOCK_VALUE_IS_VALID(PARAM) + +#endif //IOT_TIMER_DISABLE_API_PARAM_CHECK +/** @} */ + +static iot_timer_time_in_ms_t m_wall_clock = 0; +static const iot_timer_clients_list_t * m_clients = NULL; + + +uint32_t iot_timer_client_list_set(const iot_timer_clients_list_t * p_list_of_clients) +{ + VERIFY_CLIENT_LIST_IS_VALID(p_list_of_clients); + + m_clients = p_list_of_clients; + return NRF_SUCCESS; +} + + +uint32_t iot_timer_update(void) +{ + m_wall_clock += IOT_TIMER_RESOLUTION_IN_MS; + if ((0xFFFFFFFFUL - m_wall_clock) < IOT_TIMER_RESOLUTION_IN_MS) + { + m_wall_clock = IOT_TIMER_RESOLUTION_IN_MS; + } + if (m_clients != NULL) + { + uint8_t index; + for (index = 0; index < m_clients->client_list_length; index++) + { + if ((m_wall_clock % m_clients->p_client_list[index].cb_interval) == 0) + { + m_clients->p_client_list[index].iot_timer_callback(m_wall_clock); + } + } + } + return NRF_SUCCESS; +} + + +uint32_t iot_timer_wall_clock_get(iot_timer_time_in_ms_t * p_elapsed_time) +{ + NULL_PARAM_CHECK(p_elapsed_time); + + *p_elapsed_time = m_wall_clock; + return NRF_SUCCESS; +} + + +uint32_t iot_timer_wall_clock_delta_get(iot_timer_time_in_ms_t * p_past_time, \ + iot_timer_time_in_ms_t * p_delta_time) +{ + NULL_PARAM_CHECK(p_past_time); + NULL_PARAM_CHECK(p_delta_time); + VERIFY_WALL_CLOCK_VALUE_IS_VALID(p_past_time); + + if (*p_past_time == m_wall_clock) + { + *p_delta_time = 0; + } + else if (*p_past_time < m_wall_clock) + { + *p_delta_time = m_wall_clock - *p_past_time; + } + else + { + // An integer overflow of the wall clock occured since *p_past_time. + + iot_timer_time_in_ms_t max_wall_clock = (0xFFFFFFFFUL / IOT_TIMER_RESOLUTION_IN_MS) \ + * IOT_TIMER_RESOLUTION_IN_MS; + *p_delta_time = max_wall_clock - *p_past_time; // Before overflow. + *p_delta_time += m_wall_clock; // After overflow. + *p_delta_time -= IOT_TIMER_RESOLUTION_IN_MS; // Because of handling of wall clock integer overflow, see above. + } + + return NRF_SUCCESS; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_timer/iot_timer.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_timer/iot_timer.h new file mode 100644 index 0000000..d7503b3 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/iot_timer/iot_timer.h @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup iot_timer IoT Timer + * @{ + * @ingroup iot_sdk_common + * @brief Timekeeping for other modules. + * + * @details The IoT Timer stores the value of the wall clock, which represents the time elapsed + * since startup (or an integer overflow). The unit of timekeeping is milliseconds. + * The IoT Timer module is conceived to be platform independent, therefore, it does not + * have an internal tick source. An external source has to update the wall clock of the + * IoT Timer at regular intervals. + * Other modules can query the current value of the wall clock and/or can act as clients + * of the IoT Timer by subscribing to callbacks that are repeated at regular intervals. + * You can configure the module by changing the @c sdk_config.h configuration file. + */ + +#ifndef IOT_TIMER_H__ +#define IOT_TIMER_H__ + +#include <stdint.h> +#include "nrf_error.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SEC_TO_MILLISEC(PARAM) (PARAM * 1000) + +/**@brief The type of an instant in milliseconds. */ +typedef uint32_t iot_timer_time_in_ms_t; + +/**@brief IoT Timer client callback type. + * + * @param[in] wall_clock_value The value of the wall clock that triggered the callback. + * + * @retval None. + * + */ +typedef void (*iot_timer_tick_cb)(iot_timer_time_in_ms_t wall_clock_value); + +/**@brief IoT Timer client structure. + * + * @note @ref cb_interval cannot be zero. + * @note @ref cb_interval must be greater or equal to @ref IOT_TIMER_RESOLUTION_IN_MS. + * @note If greater, @ref cb_interval must be an integral multiple of @ref IOT_TIMER_RESOLUTION_IN_MS. + */ +typedef struct +{ + iot_timer_tick_cb iot_timer_callback; /**< Callback procedure of the client. */ + iot_timer_time_in_ms_t cb_interval; /**< Interval between repeated callbacks to the client. */ +} iot_timer_client_t; + +/**@brief IoT Timer client list structure. */ +typedef struct +{ + uint8_t client_list_length; /**< Total number of clients. */ + const iot_timer_client_t * p_client_list; /**< Pointer to the constant array of clients or NULL. */ +} iot_timer_clients_list_t; + +/**@brief Function for setting the list of clients that subscribe for repeated callbacks from + * the module. + * + * @note To minimize drift between client callbacks, the callback function of each client + * should be designed in a way that the duration of execution does not vary and is as short + * as possible. + * + * @param[in] p_list_of_clients Address of the client list. Can be NULL to cancel all subscriptions. + * To see what parameters are valid, please see the description + * of @ref iot_timer_client_t. + * + * @retval NRF_SUCCESS Address of the list of clients successfully updated. + * @retval NRF_ERROR_INVALID_PARAM If any member of the client list has NULL as a callback + * procedure or the interval for repeated callbacks is smaller + * or equal to @ref IOT_TIMER_RESOLUTION_IN_MS or it is not + * an integral multiple of @ref IOT_TIMER_RESOLUTION_IN_MS. + * + */ +uint32_t iot_timer_client_list_set(const iot_timer_clients_list_t * p_list_of_clients); + +/**@brief Function for updating the wall clock. + * + * @details The application designer must ensure that this function is called at regular intervals, + * which is set in the @c sdk_config.h configuration file. + * If the updated value of the wall clock is an integral multiple of the callback interval + * of any clients of the module, the callback procedure of the client is executed. + * + * @note The interrupt that triggers the update of the wall clock should have a high relative + * priority to minimize inaccuracy. + * + * @retval NRF_SUCCESS Wall clock successfully updated. + * + */ +uint32_t iot_timer_update(void); + +/**@brief Function for getting the current wall clock value. + * + * @note The accuracy of timekeeping is limited by the resolution, as set in the @c sdk_config.h + * configuration file. + * + * @param[out] p_elapsed_time Value of the wall clock. Time in milliseconds elapsed since startup + * (or an integer overflow). + * + * @retval NRF_SUCCESS Query successful. + * @retval NRF_ERROR_NULL If @b p_elapsed_time is a NULL pointer. + * + */ +uint32_t iot_timer_wall_clock_get(iot_timer_time_in_ms_t * p_elapsed_time); + +/**@brief Function for getting the difference between the current and an older wall clock value. + * + * @note The accuracy of calculation is limited by the wall clock resolution, as set in + * the @c sdk_config.h configuration file. + * @note The time difference can only be calculated correctly if only at most one integer overflow + * of the wall clock has occured since the past wall clock value was obtained. + * + * @param[in] p_past_time Past value of the wall clock. Has to be an integral multiple of + * @ref IOT_TIMER_RESOLUTION_IN_MS or zero. + * @param[out] p_delta_time Time elapsed since @b p_past_time in milliseconds. + * + * @retval NRF_SUCCESS Query successful. + * @retval NRF_ERROR_NULL If @b p_past_time or @b p_delta_time is a NULL pointer. + * @retval NRF_ERROR_INVALID_PARAM If @b p_past_time points to a value that is not an integral + * multiple of @ref IOT_TIMER_RESOLUTION_IN_MS. + * + */ +uint32_t iot_timer_wall_clock_delta_get(iot_timer_time_in_ms_t * p_past_time, \ + iot_timer_time_in_ms_t * p_delta_time); + +#ifdef __cplusplus +} +#endif + +#endif // IOT_TIMER_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_parse/ipv6_parse.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_parse/ipv6_parse.c new file mode 100644 index 0000000..0581763 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_parse/ipv6_parse.c @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stddef.h> +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include "ipv6_parse.h" +#include "nrf_error.h" + +static uint16_t ascii_to_hex(char * p_str) +{ + uint16_t res = 0; + sscanf(p_str, "%hx", &res); + return res; +} + +static void reverse_string(char * p_str) +{ + uint32_t str_len = strlen(p_str); + for (uint32_t i = 0, j = str_len - 1; i < j; i++, j--) + { + char tmp = p_str[i]; + p_str[i] = p_str[j]; + p_str[j] = tmp; + } +} + +uint32_t ipv6_parse_addr(uint8_t * p_addr, const char * p_uri, uint8_t uri_len) +{ + bool is_compressed = false; + uint8_t block_1_len = uri_len; + + const char * compressed_position = strstr(&p_uri[0], "::"); + if (compressed_position != NULL) + { + is_compressed = true; + block_1_len = compressed_position - &p_uri[0]; + } + // Parse block 1. + uint8_t block_1_end = block_1_len; + char sub_addr_buf[5] = {'\0',}; + + uint8_t char_pos = 0; + uint8_t sub_addr_count_block_1 = 0; + + for (uint8_t i = 0; i < block_1_end; i++) + { + if (p_uri[i] != ':') + { + sub_addr_buf[char_pos++] = p_uri[i]; + } + else + { + // we have read all number bytes and hit a delimiter. Save the sub address. + uint16_t value = ascii_to_hex(sub_addr_buf); + p_addr[sub_addr_count_block_1++] = ((value & 0xFF00) >> 8); + p_addr[sub_addr_count_block_1++] = ((value & 0x00FF)); + + char_pos = 0; + memset(sub_addr_buf, '\0', 5); + + } + + // if we are at the end of block 1, save the last sub address. + if ((i + 1) == block_1_end) + { + uint16_t value = ascii_to_hex(sub_addr_buf); + p_addr[sub_addr_count_block_1++] = ((value & 0xFF00) >> 8); + p_addr[sub_addr_count_block_1++] = ((value & 0x00FF)); + + + char_pos = 0; + memset(sub_addr_buf, '\0', 5); + } + } + + if (is_compressed == true) + { + // NOTE: sub_addr_buf must be cleared in previous loop. + + // lets parse backwards for second block. + uint8_t block_2_start = block_1_end + 2; // skip the '::' delimiter. + uint8_t block_2_len = uri_len - (block_1_len + 2); + uint8_t block_2_end = block_2_start + block_2_len; + + uint8_t sub_addr_count_block_2 = 0; + uint8_t sub_addr_index = 15; + + for (uint8_t i = block_2_end - 1; i > block_2_start - 1; i--) + { + if (p_uri[i] != ':') + { + sub_addr_buf[char_pos++] = p_uri[i]; + } + else + { + // we have read all number bytes and hit a delimiter. Save the sub address. + reverse_string(sub_addr_buf); + + uint16_t value = ascii_to_hex(sub_addr_buf); + p_addr[sub_addr_index--] = ((value & 0x00FF)); + p_addr[sub_addr_index--] = ((value & 0xFF00) >> 8); + sub_addr_count_block_2 += 2; + char_pos = 0; + memset(sub_addr_buf, '\0', 5); + + } + + // if we are at the end of block 1, save the last sub address. + if (i == block_2_start) + { + reverse_string(sub_addr_buf); + + uint16_t value = ascii_to_hex(sub_addr_buf); + p_addr[sub_addr_index--] = ((value & 0x00FF)); + p_addr[sub_addr_index--] = ((value & 0xFF00) >> 8); + sub_addr_count_block_2 += 2; + char_pos = 0; + memset(sub_addr_buf, '\0', 5); + } + } + + for (uint8_t i = sub_addr_count_block_1; i < (16 - sub_addr_count_block_2); i++) + { + p_addr[i] = 0x00; + } + } + return NRF_SUCCESS; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_parse/ipv6_parse.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_parse/ipv6_parse.h new file mode 100644 index 0000000..218bfdf --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_parse/ipv6_parse.h @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file ipv6_parse.h + * + * @defgroup iot_tools IoT Utility tools + * @ingroup iot_sdk_common + * @{ + * @brief Common IoT utility tools like parsing IPv6 address from a URI. + * + * @details This module provides utility functions like parsing IPv6 address from a URI. + */ +#ifndef IPV6_PARSE_H__ +#define IPV6_PARSE_H__ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief IoT IPv6 address parsing. + * + * @details Supports parsing all legal IPv6 address formats as defined by the RFC. + * + * @param[out] p_addr Pointer to array large enough to hold parsed address. MUST be 16 bytes big. + * @param[in] p_uri String with address that should be parsed. + * @param[in] uri_len Length of p_uri string. + * + */ +uint32_t ipv6_parse_addr(uint8_t * p_addr, const char * p_uri, uint8_t uri_len); + +#ifdef __cplusplus +} +#endif + +#endif // IPV6_PARSE_H__ + /** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c new file mode 100644 index 0000000..6c90ce3 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c @@ -0,0 +1,903 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include "sdk_errors.h" +#include "sdk_os.h" +#include "sdk_config.h" +#include "iot_common.h" +#include "iot_pbuffer.h" +#include "mem_manager.h" +#include "ipv6_api.h" +#include "udp_api.h" +#include "dns6_api.h" + +#if DNS6_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME dns6 + +#define NRF_LOG_LEVEL DNS6_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR DNS6_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR DNS6_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define DNS6_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define DNS6_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define DNS6_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define DNS6_ENTRY() DNS6_TRC(">> %s", __func__) +#define DNS6_EXIT() DNS6_TRC("<< %s", __func__) + +#else // DNS6_CONFIG_LOG_ENABLED + +#define DNS6_TRC(...) /**< Disables traces. */ +#define DNS6_DUMP(...) /**< Disables dumping of octet streams. */ +#define DNS6_ERR(...) /**< Disables error logs. */ + +#define DNS6_ENTRY(...) +#define DNS6_EXIT(...) + +#endif // DNS6_CONFIG_LOG_ENABLED + +/** + * @defgroup dns6_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case need arises to use an alternative architecture. + * @{ + */ +#define DNS6_MUTEX_LOCK() SDK_MUTEX_LOCK(m_dns6_mutex) /**< Lock module using mutex */ +#define DNS6_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_dns6_mutex) /**< Unlock module using mutex */ +/** @} */ + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * DNS6_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks. + * + * @{ + */ + +#if (DNS6_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Macro to check is module is initialized before requesting one of the module procedures. */ +#define VERIFY_MODULE_IS_INITIALIZED() \ + if (m_initialization_state == false) \ + { \ + return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_DNS6_ERR_BASE);\ + } + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_DNS6_ERR_BASE); \ + } + +/**@brief Verify that empty parameters are not passed to API by application. */ +#define EMPTY_PARAM_CHECK(PARAM) \ + if (*PARAM == 0) \ + { \ + return (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE); \ + } + +#else // DNS6_DISABLE_API_PARAM_CHECK + +#define VERIFY_MODULE_IS_INITIALIZED() +#define NULL_PARAM_CHECK(PARAM) +#define EMPTY_PARAM_CHECK(PARAM) + +#endif // DNS6_DISABLE_API_PARAM_CHECK +/** @} */ + +/**@brief RFC1035 - DNS Header Fields and Values. */ +#define DNS_HEADER_FLAG1_QR_QUERY 0x00 /**< Bit specifies that message is a query. */ +#define DNS_HEADER_FLAG1_QR_RESPONSE 0x80 /**< Bit specifies that message is a response. */ +#define DNS_HEADER_FLAG1_OPCODE_STANDARD 0x00 /**< A standard type of query. */ +#define DNS_HEADER_FLAG1_OPCODE_INVERSE 0x08 /**< An inverse type of query. */ +#define DNS_HEADER_FLAG1_OPCODE_STATUS 0x10 /**< A server status request. */ +#define DNS_HEADER_FLAG1_AA 0x04 /**< Bit specifies that the responding name server is an authority for the domain name in question section. */ +#define DNS_HEADER_FLAG1_TC 0x02 /**< Bit specifies that message is truncated. */ +#define DNS_HEADER_FLAG1_RD 0x01 /**< Bit specifies that recursion is desired. */ + +#define DNS_HEADER_FLAG2_RA 0x80 /**< Bit specifies if recursive query support is available in the name server. */ +#define DNS_HEADER_FLAG2_RCODE_NONE 0x00 /**< No error condition. */ +#define DNS_HEADER_FLAG2_RCODE_FORMAT_ERROR 0x01 /**< Error indicates that dns server is unable o interpret the query. */ +#define DNS_HEADER_FLAG2_RCODE_SERVER_FAILURE 0x02 /**< Error indicates that dns server has internal problem. */ +#define DNS_HEADER_FLAG2_RCODE_NAME_ERROR 0x03 /**< Error indicates that domain name referenced in the query does not exist. */ +#define DNS_HEADER_FLAG2_RCODE_NOT_IMPLEMENTED 0x04 /**< Error indicates that dns server does not support previously sent query. */ +#define DNS_HEADER_FLAG2_RCODE_REFUSED 0x05 /**< Error indicates that dns server refuses to perform operation. */ +#define DNS_HEADER_FLAG2_RCODE_MASK 0x0F /**< Bit mask of RCODE field. */ + +#define DNS_QTYPE_A 0x0001 /**< QTYPE indicates IPv4 address. */ +#define DNS_QTYPE_CNAME 0x0005 /**< QTYPE indicates CNAME record. */ +#define DNS_QTYPE_AAAA 0x001C /**< QTYPE indicates IPv6 address. */ + +#define DNS_QCLASS_IN 0x0001 /**< QCLASS indicates Internet type. */ + +/**@brief DNS6 client module's defines. */ +#define DNS_LABEL_SEPARATOR '.' /**< Separator of hostname string. */ +#define DNS_LABEL_OFFSET 0xc0 /**< Byte indicates that offset is used to determine hostname. */ + +#define DNS_HEADER_SIZE 12 /**< Size of DNS Header. */ +#define DNS_QUESTION_FOOTER_SIZE 4 /**< Size of DNS Question footer. */ +#define DNS_RR_BODY_SIZE 10 /**< Size of DNS Resource Record Body. */ + +#define MESSAGE_ID_UNUSED 0 /**< Value indicates that record is unused and no request was performed yet. */ +#define MESSAGE_ID_INITIAL 0x0001 /**< Initial value of message id counter. */ + + +/**@brief DNS Header Format. */ +typedef struct +{ + uint16_t msg_id; /**< Query/Response message identifier. */ + uint8_t flags_1; /**< Flags ( QR | Opcode | AA | TC | RD ). */ + uint8_t flags_2; /**< Flags ( RA | Z | RCODE ). */ + uint16_t qdcount; /**< The number of entries in the question section. */ + uint16_t ancount; /**< The number of resource records in the answer section. */ + uint16_t nscount; /**< The number of name server resource records in the authority records section. */ + uint16_t arcount; /**< The number of resource records in the additional records section. */ +} dns_header_t; + +/**@brief DNS Question Footer Format. */ +typedef struct +{ + uint16_t qtype; /**< Type of the query. */ + uint16_t qclass; /**< Class of the query. */ +} dns_question_footer_t; + +/**@brief DNS Resource AAAA Record Body Format. */ +typedef struct +{ + uint16_t rtype; /**< Type of the response. */ + uint16_t rclass; /**< Class of the response. */ + uint32_t rttl; /**< Time to Life field of the response. */ + uint16_t rdlength; /**< Length of data in octets. */ +} dns_rr_body_t; + +/**@brief Structure holds pending query. */ +typedef struct +{ + uint16_t message_id; /**< Message id for DNS Query. */ + uint8_t retries; /**< Number of already performed retries. */ + uint8_t * p_hostname; /**< Pointer to hostname string in memory menager.*/ + iot_timer_time_in_ms_t next_retransmission; /**< Time when next retransmission should be invoked. */ + dns6_evt_handler_t evt_handler; /**< User registered callback. */ +} pending_query_t; + +SDK_MUTEX_DEFINE(m_dns6_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ +static bool m_initialization_state = false; /**< Variable to maintain module initialization state. */ +static pending_query_t m_pending_queries[DNS6_MAX_PENDING_QUERIES]; /**< Queue contains pending queries. */ +static uint16_t m_message_id_counter; /**< Message ID counter, used to generate unique message IDs. */ +static udp6_socket_t m_socket; /**< Socket information provided by UDP. */ + + +/**@brief Function for freeing query entry in pending queue. + * + * @param[in] index Index of query. + * + * @retval None. + */ +static void query_init(uint32_t index) +{ + if (m_pending_queries[index].p_hostname) + { + UNUSED_VARIABLE(nrf_free(m_pending_queries[index].p_hostname)); + } + + m_pending_queries[index].message_id = MESSAGE_ID_UNUSED; + m_pending_queries[index].retries = 0; + m_pending_queries[index].p_hostname = NULL; + m_pending_queries[index].evt_handler = NULL; + m_pending_queries[index].next_retransmission = 0; +} + + +/**@brief Function for adding new query to pending queue. + * + * @param[in] p_hostname Pointer to hostname string. + * @param[in] evt_handler User defined event to handle given query. + * + * @retval Index of element in pending queries' table or DNS6_MAX_PENDING_QUERIES if no memory. + */ +static uint32_t query_add(uint8_t * p_hostname, dns6_evt_handler_t evt_handler) +{ + uint32_t index; + + for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++) + { + if (m_pending_queries[index].message_id == MESSAGE_ID_UNUSED) + { + m_pending_queries[index].message_id = m_message_id_counter++; + m_pending_queries[index].retries = 0; + m_pending_queries[index].p_hostname = p_hostname; + m_pending_queries[index].evt_handler = evt_handler; + m_pending_queries[index].next_retransmission = 0; + + break; + } + } + + return index; +} + + +/**@brief Function for finding element in pending queue with specific message_id. + * + * @param[in] message_id Message identifier to find. + * + * @retval Index of element in pending queue or DNS6_MAX_PENDING_QUERIES if nothing found. + */ +static uint32_t query_find(uint32_t message_id) +{ + uint32_t index; + + for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++) + { + if (m_pending_queries[index].message_id == message_id) + { + break; + } + } + + return index; +} + + +/**@brief Function for checking if retransmission time of DNS query has been expired. + * + * @param[in] index Index of pending query. + * + * @retval True if timer has been expired, False otherwise. + */ +static bool query_timer_is_expired(uint32_t index) +{ + uint32_t err_code; + iot_timer_time_in_ms_t wall_clock_value; + + // Get wall clock time. + err_code = iot_timer_wall_clock_get(&wall_clock_value); + + if (err_code == NRF_SUCCESS) + { + if (wall_clock_value >= m_pending_queries[index].next_retransmission) + { + return true; + } + } + + return false; +} + + +/**@brief Function for setting retransmissions time of DNS query has been expired. + * + * @param[in] index Index of pending query. + * + * @retval None. + */ +static void query_timer_set(uint32_t index) +{ + uint32_t err_code; + iot_timer_time_in_ms_t wall_clock_value; + + // Get wall clock time. + err_code = iot_timer_wall_clock_get(&wall_clock_value); + + if (err_code == NRF_SUCCESS) + { + m_pending_queries[index].next_retransmission = + wall_clock_value + (DNS6_RETRANSMISSION_INTERVAL * 1000); + } +} + + +/**@brief Function for creating compressed hostname from string. + * + * @param[inout] p_dest Pointer to place where hostname will be compressed. + * @param[in] p_hostname Pointer to hostname string. + * + * @retval Number of used bytes to compress a hostname. + */ +static uint32_t compress_hostname(uint8_t * p_dest, const uint8_t * p_hostname) +{ + uint32_t index = 0; + uint32_t label_pos = 0; + uint8_t * p_original = p_dest; + + // Elide first byte in destination buffer to put label. + p_dest++; + + // Parse until string termination is found. + for (index = 0; p_hostname[index] != 0; index++) + { + // Look for string separator. + if (p_hostname[index] == DNS_LABEL_SEPARATOR) + { + // Put number of subsequent string to last label. + p_original[label_pos] = index - label_pos; + + // Protection to stop compressing after getting incorrect sequence. + if (index == label_pos) + { + return index + 1; + } + + label_pos = index + 1; + } + else + { + // Copy character of hostname to destination buffer. + *p_dest = p_hostname[index]; + } + + p_dest++; + } + + // Set last label. + p_original[label_pos] = index - label_pos; + + // Terminate compressed hostname with 0. + *p_dest = 0; + + // Return length of compressed string. + return index + 2; +} + + +/**@brief Function for finding end of compressed hostname. + * + * @param[in] p_hostname Pointer to compressed hostname string. + * + * @retval Pointer to the end of compressed hostname. + */ +static uint8_t * skip_compressed_hostname(uint8_t * p_hostname) +{ + while (*p_hostname != 0) + { + if ((*p_hostname & DNS_LABEL_OFFSET) == DNS_LABEL_OFFSET) + { + return p_hostname + 2; + } + else + { + p_hostname += *p_hostname + 1; + } + } + + return p_hostname + 1; +} + + +/**@brief Function for sending DNS query. + * + * @param[in] index Index of query. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t query_send(uint32_t index) +{ + uint32_t length; + uint32_t err_code; + iot_pbuffer_t * p_buffer; + iot_pbuffer_alloc_param_t buffer_param; + + buffer_param.type = UDP6_PACKET_TYPE; + buffer_param.flags = PBUFFER_FLAG_DEFAULT; + buffer_param.length = DNS_HEADER_SIZE + DNS_QUESTION_FOOTER_SIZE + + strlen((const char *)m_pending_queries[index].p_hostname) + 2; + + // Allocate packet buffer. + err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer); + + if (err_code == NRF_SUCCESS) + { + const dns_question_footer_t question_footer = + { + .qtype = HTONS(DNS_QTYPE_AAAA), + .qclass = HTONS(DNS_QCLASS_IN) + }; + + dns_header_t * p_dns_header = (dns_header_t *)p_buffer->p_payload; + + // Fill DNS header fields. + p_dns_header->msg_id = HTONS(m_pending_queries[index].message_id); + p_dns_header->flags_1 = DNS_HEADER_FLAG1_QR_QUERY | DNS_HEADER_FLAG1_RD; + p_dns_header->flags_2 = DNS_HEADER_FLAG2_RCODE_NONE; + + // Send only one question. + p_dns_header->qdcount = HTONS(1); + p_dns_header->ancount = HTONS(0); + p_dns_header->nscount = HTONS(0); + p_dns_header->arcount = HTONS(0); + + // Start indexing from the end of the DNS header. + length = DNS_HEADER_SIZE; + + // Compress and put hostname. + length += compress_hostname(&p_buffer->p_payload[length], + m_pending_queries[index].p_hostname); + + // Add question footer. + memcpy(&p_buffer->p_payload[length], (uint8_t *)&question_footer, DNS_QUESTION_FOOTER_SIZE); + + length += DNS_QUESTION_FOOTER_SIZE; + + // Update packet buffer's data length. + p_buffer->length = length; + + // Set retransmission timer. + query_timer_set(index); + + // Send DNS query using UDP socket. + err_code = udp6_socket_send(&m_socket, p_buffer); + + if (err_code != NRF_SUCCESS) + { + DNS6_ERR("Unable to send query on UDP socket. Reason %08lx.", err_code); + + // Free the allocated buffer as send procedure has failed. + UNUSED_VARIABLE(iot_pbuffer_free(p_buffer, true)); + } + } + else + { + DNS6_ERR("No memory to allocate packet buffer."); + } + + return err_code; +} + + +/**@brief Function for notifying application of the DNS6 query status. + * + * @param[in] index Index of query. + * @param[in] process_result Variable indicates result of DNS query. + * @param[in] p_addr Pointer to memory that holds IPv6 addresses. + * @param[in] addr_count Number of found addresses. + * + * @retval None. + */ +static void app_notify(uint32_t index, + uint32_t process_result, + ipv6_addr_t * p_addr, + uint16_t addr_count) +{ + if (m_pending_queries[index].evt_handler) + { + DNS6_MUTEX_UNLOCK(); + + // Call handler of user request. + m_pending_queries[index].evt_handler(process_result, + (const char *)m_pending_queries[index].p_hostname, + p_addr, + addr_count); + + DNS6_MUTEX_LOCK(); + } +} + + +/**@brief Callback handler to receive data on the UDP port. + * + * @param[in] p_socket Socket identifier. + * @param[in] p_ip_header IPv6 header containing source and destination addresses. + * @param[in] p_udp_header UDP header identifying local and remote endpoints. + * @param[in] process_result Result of data reception, there could be possible errors like + * invalid checksum etc. + * @param[in] p_rx_packet Packet buffer containing the received data packet. + * + * @retval NRF_SUCCESS Indicates received data was handled successfully, else an an + * error code indicating reason for failure.. + */ +static uint32_t server_response(const udp6_socket_t * p_socket, + const ipv6_header_t * p_ip_header, + const udp6_header_t * p_udp_header, + uint32_t process_result, + iot_pbuffer_t * p_rx_packet) +{ + uint32_t index; + uint32_t rr_index; + uint32_t err_code = NRF_SUCCESS; + ipv6_addr_t * p_addresses = NULL; + uint16_t addr_length = 0; + + DNS6_MUTEX_LOCK(); + + DNS6_ENTRY(); + + // Check UDP process result and data length. + if ((process_result != NRF_SUCCESS) || p_rx_packet->length < DNS_HEADER_SIZE) + { + DNS6_ERR("Received erroneous response."); + err_code = (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE); + } + else + { + dns_header_t * p_dns_header = (dns_header_t *)p_rx_packet->p_payload; + uint8_t * p_data = &p_rx_packet->p_payload[DNS_HEADER_SIZE]; + uint16_t qdcount = NTOHS(p_dns_header->qdcount); + uint16_t ancount = NTOHS(p_dns_header->ancount); + + // Try to find a proper query for this response, else discard. + index = query_find(NTOHS(p_dns_header->msg_id)); + + if (index != DNS6_MAX_PENDING_QUERIES) + { + DNS6_TRC("Received response for hostname %s with %d answers.", + m_pending_queries[index].p_hostname, ancount); + + // Check truncation error. + if (p_dns_header->flags_1 & DNS_HEADER_FLAG1_TC) + { + err_code = DNS6_RESPONSE_TRUNCATED; + } + else if (!(p_dns_header->flags_1 & DNS_HEADER_FLAG1_QR_RESPONSE)) + { + err_code = (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE); + } + // Check response code. + else if (p_dns_header->flags_2 & DNS_HEADER_FLAG2_RCODE_MASK) + { + switch (p_dns_header->flags_2 & DNS_HEADER_FLAG2_RCODE_MASK) + { + case DNS_HEADER_FLAG2_RCODE_FORMAT_ERROR: + err_code = DNS6_FORMAT_ERROR; + break; + + case DNS_HEADER_FLAG2_RCODE_SERVER_FAILURE: + err_code = DNS6_SERVER_FAILURE; + break; + + case DNS_HEADER_FLAG2_RCODE_NAME_ERROR: + err_code = DNS6_HOSTNAME_NOT_FOUND; + break; + + case DNS_HEADER_FLAG2_RCODE_NOT_IMPLEMENTED: + err_code = DNS6_NOT_IMPLEMENTED; + break; + + case DNS_HEADER_FLAG2_RCODE_REFUSED: + err_code = DNS6_REFUSED_ERROR; + break; + + default: + err_code = (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE); + break; + } + } + else if (ancount == 0) + { + // No answer found. + err_code = DNS6_HOSTNAME_NOT_FOUND; + } + else + { + dns_rr_body_t rr; + + // Skip questions section. + for (rr_index = 0; rr_index < qdcount; rr_index++) + { + p_data = skip_compressed_hostname(p_data) + DNS_QUESTION_FOOTER_SIZE; + } + + // Addresses are moved to beginning of the packet to ensure alignment is correct. + p_addresses = (ipv6_addr_t *)p_rx_packet->p_payload; + + // Parse responses section. + for (rr_index = 0; rr_index < ancount; rr_index++) + { + p_data = skip_compressed_hostname(p_data); + + // Fill resource record structure to fit alignment. + memcpy((uint8_t *)&rr, p_data, DNS_RR_BODY_SIZE); + + if (NTOHS(rr.rtype) == DNS_QTYPE_AAAA && NTOHS(rr.rclass) == DNS_QCLASS_IN) + { + if (NTOHS(rr.rdlength) == IPV6_ADDR_SIZE) + { + DNS6_TRC("Found AAAA record with IPv6 address:"); + DNS6_DUMP(p_data + DNS_RR_BODY_SIZE, IPV6_ADDR_SIZE); + + // Move all addresses next to each other. + memmove(p_addresses[addr_length].u8, + p_data + DNS_RR_BODY_SIZE, + IPV6_ADDR_SIZE); + + addr_length++; + } + } + + p_data += DNS_RR_BODY_SIZE + NTOHS(rr.rdlength); + } + + if (addr_length == 0) + { + DNS6_ERR("No IPv6 addresses was found."); + + err_code = DNS6_HOSTNAME_NOT_FOUND; + } + } + + // Notify application. + app_notify(index, err_code, p_addresses, addr_length); + + // Initialize query entry. + query_init(index); + } + else + { + DNS6_ERR("Response with unknown message id."); + err_code = (NRF_ERROR_NOT_FOUND | IOT_DNS6_ERR_BASE); + } + } + + DNS6_EXIT(); + + DNS6_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t dns6_init(const dns6_init_t * p_dns_init) +{ + NULL_PARAM_CHECK(p_dns_init); + + uint32_t index; + uint32_t err_code; + + DNS6_ENTRY(); + + SDK_MUTEX_INIT(m_dns6_mutex); + + DNS6_MUTEX_LOCK(); + + for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++) + { + query_init(index); + } + + // Request new socket creation. + err_code = udp6_socket_allocate(&m_socket); + + if (err_code == NRF_SUCCESS) + { + // Bind the socket to the local port. + err_code = udp6_socket_bind(&m_socket, IPV6_ADDR_ANY, p_dns_init->local_src_port); + + if (err_code == NRF_SUCCESS) + { + // Connect to DNS server. + err_code = udp6_socket_connect(&m_socket, + &p_dns_init->dns_server.addr, + p_dns_init->dns_server.port); + + if (err_code == NRF_SUCCESS) + { + // Register data receive callback. + err_code = udp6_socket_recv(&m_socket, server_response); + } + } + + if (err_code == NRF_SUCCESS) + { + DNS6_TRC("Module initialization is complete."); + + // Set initialization state flag if all procedures succeeded. + m_initialization_state = true; + m_message_id_counter = 0x0001; + } + else + { + DNS6_ERR("UDP socket initialization failed. Reason %08lx.", err_code); + + // Not all procedures succeeded with allocated socket, hence free it. + UNUSED_VARIABLE(udp6_socket_free(&m_socket)); + } + } + + DNS6_EXIT(); + + DNS6_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t dns6_uninit(void) +{ + VERIFY_MODULE_IS_INITIALIZED(); + + uint32_t index; + + DNS6_ENTRY(); + + DNS6_MUTEX_LOCK(); + + for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++) + { + query_init(index); + } + + // Free UDP socket. + UNUSED_VARIABLE(udp6_socket_free(&m_socket)); + + // Clear initialization state flag. + m_initialization_state = false; + + DNS6_EXIT(); + + DNS6_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t dns6_query(const char * p_hostname, dns6_evt_handler_t evt_handler) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(evt_handler); + NULL_PARAM_CHECK(p_hostname); + EMPTY_PARAM_CHECK(p_hostname); + + uint32_t index; + uint32_t err_code; + uint32_t hostname_length; + uint8_t * p_hostname_buff = NULL; + + DNS6_ENTRY(); + + DNS6_MUTEX_LOCK(); + + // Calculate hostname length. + hostname_length = strlen(p_hostname) + 1; + + // Allocate memory to make copy of hostname string. + err_code = nrf_mem_reserve(&p_hostname_buff, &hostname_length); + + if (err_code == NRF_SUCCESS) + { + // Copy hostname to cache buffer. + strcpy((char *)p_hostname_buff, p_hostname); + + // Add query to pending queue. + index = query_add(p_hostname_buff, evt_handler); + + if (index != DNS6_MAX_PENDING_QUERIES) + { + // Create and send DNS Query. + err_code = query_send(index); + + if (err_code != NRF_SUCCESS) + { + // Remove query from pending queue immediately. + query_init(index); + } + } + else + { + DNS6_ERR("No place in pending queue."); + + // No place in pending queue. + err_code = (NRF_ERROR_NO_MEM | IOT_DNS6_ERR_BASE); + } + + // Not all procedures succeeded with sending query, hence free buffer for hostname. + if (err_code != NRF_SUCCESS) + { + UNUSED_VARIABLE(nrf_free(p_hostname_buff)); + } + } + else + { + DNS6_ERR("No memory to allocate buffer for hostname."); + } + + DNS6_EXIT(); + + DNS6_MUTEX_UNLOCK(); + + return err_code; +} + + +void dns6_timeout_process(iot_timer_time_in_ms_t wall_clock_value) +{ + uint32_t index; + uint32_t err_code; + + UNUSED_PARAMETER(wall_clock_value); + + DNS6_ENTRY(); + + DNS6_MUTEX_LOCK(); + + for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++) + { + if (m_pending_queries[index].message_id != MESSAGE_ID_UNUSED) + { + if (query_timer_is_expired(index)) + { + err_code = NRF_SUCCESS; + + if (m_pending_queries[index].retries < DNS6_MAX_RETRANSMISSION_COUNT) + { + DNS6_TRC("Query retransmission [%d] for hostname %s.", + m_pending_queries[index].retries, m_pending_queries[index].p_hostname); + + // Increase retransmission number. + m_pending_queries[index].retries++; + + // Send query again. + err_code = query_send(index); + } + else + { + DNS6_ERR("DNS server did not response on query for hostname %s.", + m_pending_queries[index].p_hostname); + + // No response from server. + err_code = DNS6_SERVER_UNREACHABLE; + } + + if (err_code != NRF_SUCCESS) + { + // Inform application that timeout occurs. + app_notify(index, err_code, NULL, 0); + + // Remove query from pending queue. + query_init(index); + } + } + break; + } + } + + DNS6_EXIT(); + + DNS6_MUTEX_UNLOCK(); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c new file mode 100644 index 0000000..8c136b8 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.c @@ -0,0 +1,1355 @@ +/** + * Copyright (c) 2013 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include "icmp6_api.h" +#include "ipv6_api.h" +#include "icmp6.h" +#include "iot_context_manager.h" +#include "ipv6_utils.h" +#include "iot_common.h" + +#if ICMP6_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME icmp6 + +#define NRF_LOG_LEVEL ICMP6_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR ICMP6_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR ICMP6_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define ICMP6_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define ICMP6_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define ICMP6_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define ICMP6_ENTRY() ICMP6_TRC(">> %s", __func__) +#define ICMP6_EXIT() ICMP6_TRC("<< %s", __func__) + +#else // ICMP6_CONFIG_LOG_ENABLED + +#define ICMP6_TRC(...) /**< Disables traces. */ +#define ICMP6_DUMP(...) /**< Disables dumping of octet streams. */ +#define ICMP6_ERR(...) /**< Disables error logs. */ + +#define ICMP6_ENTRY(...) +#define ICMP6_EXIT(...) + +#endif // ICMP6_CONFIG_LOG_ENABLED + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * ICMP6_DISABLE_API_PARAM_CHECK should be set to 1 to disable these checks. + * + * @{ + */ +#if (ICMP6_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Macro to check is module is initialized before requesting one of the module procedures. */ +#define VERIFY_MODULE_IS_INITIALIZED() \ + if (m_initialization_state == false) \ + { \ + return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_ICMP6_ERR_BASE); \ + } + +/**@brief Macro to check is module is initialized before requesting one of the module + procedures but does not use any return code. */ +#define VERIFY_MODULE_IS_INITIALIZED_VOID() \ + if (m_initialization_state == false) \ + { \ + return; \ + } + +/** + * @brief Verify NULL parameters are not passed to API by application. + */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_ICMP6_ERR_BASE); \ + } + +/** + * @brief Verify packet buffer is of ICMP6 Type. + */ +#define PACKET_TYPE_CHECK(PACKET) \ + if ((PACKET)->type != ICMP6_PACKET_TYPE) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_ICMP6_ERR_BASE); \ + } + + +#else // ICMP6_DISABLE_API_PARAM_CHECK + +#define VERIFY_MODULE_IS_INITIALIZED() +#define VERIFY_MODULE_IS_INITIALIZED_VOID() +#define NULL_PARAM_CHECK(PARAM) +#define PACKET_TYPE_CHECK(PACKET) + +#endif // ICMP6_DISABLE_API_PARAM_CHECK +/** @} */ + +/** + * @defgroup icmp6_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case need arises to use an alternative architecture. + * @{ + */ +#define ICMP6_MUTEX_LOCK() SDK_MUTEX_LOCK(m_icmp6_mutex) /**< Lock module using mutex */ +#define ICMP6_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_icmp6_mutex) /**< Unlock module using mutex */ +/** @} */ + +#define ND_NS_HEADER_SIZE 20 /**< Size of Neighbour Solicitation message. */ +#define ND_NA_HEADER_SIZE 20 /**< Size of Neighbour Advertisement message. */ +#define ND_RS_HEADER_SIZE 4 /**< Size of Router Solicitation message. */ +#define ND_RA_HEADER_SIZE 12 /**< Size of Router Advertisement message. */ +#define ND_PAYLOAD_ADJUST_OFFSET 4 /**< Adjusting ND related payload offset as the general ICMP structure is not upheld. */ + +#define ND_NA_R_FLAG 0x80 /**< Router flag. When set, the R-bit indicates that the sender is a router. */ +#define ND_NA_S_FLAG 0x40 /**< Solicited flag. When set, the S-bit indicates that the advertisement was sent in response + to a Neighbor Solicitation .*/ +#define ND_NA_O_FLAG 0x20 /**< Override flag. When set, the O-bit indicates that the advertisement should override + an existing cache entry and update the cached link-layer address .*/ + +#define ND_OPT_TYPE_SLLAO 1 /**< Source Link Layer Address Option. */ +#define ND_OPT_TYPE_TLLAO 2 /**< Target Link Layer Address Option. */ +#define ND_OPT_TYPE_PIO 3 /**< Prefix Information Option. */ +#define ND_OPT_TYPE_RHO 4 /**< Redirected Header Option. */ +#define ND_OPT_TYPE_MTU 5 /**< Maximum Transmit Unit Option. */ +#define ND_OPT_TYPE_ARO 33 /**< Address Registration Option. */ +#define ND_OPT_TYPE_6CO 34 /**< 6LoWPAN Context Option. */ +#define ND_OPT_TYPE_6ABRO 35 /**< Authoritative Border Router Option. */ + +#define ND_OPT_SLLAO_SIZE (8 * (((IPV6_LL_ADDR_SIZE) / 8) + 1)) /**< Size of SLLAO option. */ +#define ND_OPT_TLLAO_SIZE (8 * (((IPV6_LL_ADDR_SIZE) / 8) + 1)) /**< Size of TLLAO option. */ +#define ND_OPT_PIO_SIZE 32 /**< Size of PIO option. */ +#define ND_OPT_MTU_SIZE 8 /**< Size of MTU option. */ +#define ND_OPT_ARO_SIZE 16 /**< Size of ARO option. */ +#define ND_OPT_6CO_SIZE 24 /**< Size of 6CO option. */ +#define ND_OPT_6ABRO_SIZE 24 /**< Size of 6ABRO option. */ + +#define ND_OPT_SLLAO_LENGTH ((ND_OPT_SLLAO_SIZE) / 8) /**< Value of length field in SLLAO option. */ +#define ND_OPT_TLLAO_LENGTH ((ND_OPT_TLLAO_SIZE) / 8) /**< Value of length field in SLLAO option. */ +#define ND_OPT_ARO_LENGTH 2 /**< Value of length field in ARO option. */ + +#define ND_OPT_6CO_CID_MASK 0x0F +#define ND_OPT_6CO_CID_POS 0 +#define ND_OPT_6CO_C_MASK 0x10 +#define ND_OPT_6CO_C_POS 4 + +#define ND_OPT_PIO_L_MASK 0x80 +#define ND_OPT_PIO_L_POS 7 +#define ND_OPT_PIO_A_MASK 0x40 +#define ND_OPT_PIO_A_POS 6 + +#define ND_HOP_LIMIT 255 /**< Value of Hop Limit used in Neighbour Discovery procedure. */ + +#define ICMP6_OFFSET IPV6_IP_HEADER_SIZE + ICMP6_HEADER_SIZE /**< Offset of ICMPv6 packet type. */ + +#define ERROR_ADDITIONAL_HEADER_SIZE 4 /**< Additional 4 bytes of information every ICMP error message contains. */ +#define ERROR_MESSAGE_HEADER_SIZE (ICMP6_HEADER_SIZE + ERROR_ADDITIONAL_HEADER_SIZE) /**< Error message header size including type, code, checksum and 32-bit parameter. */ +#define ICMP6_ERROR_OFFSET IPV6_IP_HEADER_SIZE + ERROR_MESSAGE_HEADER_SIZE /**< Offset for ICMPv6 error message. */ + +/**@brief Neighbor Solicitation header. */ +typedef struct +{ + uint32_t reserved; /**< Reserved field. */ + ipv6_addr_t target_addr; /**< Target Address field. */ +} icmp6_ns_header_t; + +/**@brief Neighbor Advertisement header. */ +typedef struct +{ + uint8_t flags; /**< Flags (R,S and O). */ + uint8_t reserved; /**< Reserved field. */ + ipv6_addr_t target_addr; /**< Target Address field. */ +} icmp6_na_header_t; + +/**@brief Router Solicitation message's header. */ +typedef struct +{ + uint32_t reserved; /**< Reserved field. */ +} icmp6_rs_header_t; + +/**@brief Option header of ICMPv6 packet. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, in unit of 8 octets. */ +} nd_option_t; + +/**@brief Source Link Layer Address Option header format. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, units of 8 octets. */ + eui64_t addr; /**< Link-layer address. */ + uint8_t padding[6]; /**< Padding. */ +} nd_option_sllao_t; + +/**@brief Target Link Layer Address Option header format. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, units of 8 octets. */ + eui64_t addr; /**< Link-layer address. */ + uint8_t padding[6]; /**< Padding. */ +} nd_option_tllao_t; + +/**@brief Prefix Information Option header format. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, units of 8 octets. */ + uint8_t prefix_length; /**< Prefix length. */ + uint8_t flags; /**< Flags (L/A) and reserved. */ + uint32_t valid_lifetime; /**< Valid Lifetime. */ + uint32_t preferred_lifetime; /**< Preferred Lifetime. */ + uint32_t reserved; /**< Reserved field. */ + ipv6_addr_t prefix; /**< Prefix address. */ +} nd_option_pio_t; + +/**@brief Address Registration Option header format. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, units of 8 octets. */ + uint8_t status; /**< Status of ARO. */ + uint8_t reserved; /**< Reserved1, split to avoid alignment. */ + uint16_t reserved2; /**< Reserved2, split to avoid alignment. */ + uint16_t registration_lifetime; /**< Registration Lifetime. */ + eui64_t eui64; /**< EUI-64 source address. */ +} nd_option_aro_t; + +/**@brief 6LoWPAN Context Option header format. */ +typedef struct +{ + uint8_t type; /**< Option type. */ + uint8_t length; /**< Length, units of 8 octets. */ + uint8_t context_length; /**< Context Length. */ + uint8_t CID_C; /**< 4-bit Context and 1-bit context compression flag. */ + uint16_t reserved; /**< Reserved. */ + uint16_t valid_lifetime; /**< Valid Lifetime. */ + ipv6_addr_t context; /**< Context IPv6 Prefix. */ +} nd_option_6co_t; + +static bool m_initialization_state = false; /**< Variable to maintain module initialization state. */ +static uint16_t m_sequence_number = 0; /**< Sequence number from ICMPv6 packet. */ +static icmp6_receive_callback_t m_event_handler = NULL; /**< Application event handler. */ +SDK_MUTEX_DEFINE(m_icmp6_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ + +/**@brief Function for initializing default values of IP Header for ICMP. + * + * @param[in] p_ip_header Pointer to IPv6 header. + * @param[in] hoplimit Hop Limit in IPv6 header. + * + * @return None. + */ +static __INLINE void icmp_ip_header(ipv6_header_t * p_ip_header, uint8_t hoplimit) +{ + ipv6_header_init(p_ip_header); + p_ip_header->next_header = IPV6_NEXT_HEADER_ICMP6; + p_ip_header->hoplimit = hoplimit; +} + +/**@brief Function for adding SLLAO option to the packet. + * + * @param[in] p_interface Pointer to IoT interface. + * @param[in] p_data Pointer to the memory where SLLAO option should be added. + * + * @return None. + */ +static __INLINE void add_sllao_opt(const iot_interface_t * p_interface, nd_option_sllao_t * p_sllao) +{ + p_sllao->type = ND_OPT_TYPE_SLLAO; + p_sllao->length = ND_OPT_SLLAO_LENGTH; + +#if (IPV6_LL_ADDR_SIZE == 6) + memcpy(p_sllao->addr.identifier, p_interface->local_addr.identifier, 3); + memcpy(p_sllao->addr.identifier + 3, p_interface->local_addr.identifier + 5, 3); +#else + // Copy EUI-64 and add padding. + memcpy(p_sllao->addr.identifier, p_interface->local_addr.identifier, IPV6_LL_ADDR_SIZE); + memset(p_sllao->padding, 0, 6); +#endif +} + +/**@brief Function for adding TLLAO option to the packet. + * + * @param[in] p_interface Pointer to IoT interface. + * @param[in] p_data Pointer to the memory where TLLAO option should be added. + * + * @return None. + */ +static __INLINE void add_tllao_opt(const iot_interface_t * p_interface, nd_option_tllao_t * p_tllao) +{ + p_tllao->type = ND_OPT_TYPE_TLLAO; + p_tllao->length = ND_OPT_TLLAO_LENGTH; + +#if (IPV6_LL_ADDR_SIZE == 6) + memcpy(p_tllao->addr.identifier, p_interface->local_addr.identifier, 3); + memcpy(p_tllao->addr.identifier + 3, p_interface->local_addr.identifier + 5, 3); +#else + // Copy EUI-64 and add padding. + memcpy(p_tllao->addr.identifier, p_interface->local_addr.identifier, IPV6_LL_ADDR_SIZE); + memset(p_tllao->padding, 0, 6); +#endif +} + +/**@brief Function for adding ARO option to packet. + * + * @param[in] p_interface Pointer to IoT interface. + * @param[in] p_data Pointer to the memory where ARO option should be added. + * @param[in] aro_lifetime Lifetime of registration. + * + * @return None. + */ +static __INLINE void add_aro_opt(const iot_interface_t * p_interface, + nd_option_aro_t * p_aro, + uint16_t aro_lifetime) +{ + p_aro->type = ND_OPT_TYPE_ARO; + p_aro->length = ND_OPT_ARO_LENGTH; + p_aro->status = 0x00; + p_aro->reserved = 0x00; + p_aro->reserved2 = 0x00; + p_aro->registration_lifetime = HTONS(aro_lifetime); + + // Copy EUI-64 and add padding. + memcpy(p_aro->eui64.identifier, p_interface->local_addr.identifier, EUI_64_ADDR_SIZE); +} + +#if (ICMP6_ENABLE_ND6_MESSAGES_TO_APPLICATION == 1 || ICMP6_ENABLE_ALL_MESSAGES_TO_APPLICATION == 1) + +/**@brief Function for notifying application of the ICMPv6 received packet. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_pbuffer Pointer to packet buffer of ICMP6_PACKET_TYPE. + * @param[in] process_result Result of internal processing packet. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static uint32_t app_notify_icmp_data(iot_interface_t * p_interface, + iot_pbuffer_t * p_pbuffer, + uint32_t process_result) +{ + uint32_t err_code = NRF_SUCCESS; + + if (m_event_handler != NULL) + { + + ipv6_header_t * p_ip_header = (ipv6_header_t *) + (p_pbuffer->p_payload - ICMP6_HEADER_SIZE - IPV6_IP_HEADER_SIZE); + icmp6_header_t * p_icmp_header = (icmp6_header_t *) + (p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + + ICMP6_MUTEX_UNLOCK(); + + // Change byte order of ICMP header given to application. + p_icmp_header->checksum = NTOHS(p_icmp_header->checksum); + + err_code = m_event_handler(p_interface, + p_ip_header, + p_icmp_header, + process_result, + p_pbuffer); + + ICMP6_MUTEX_LOCK(); + } + + return err_code; +} + +#endif + +#if (ICMP6_ENABLE_HANDLE_ECHO_REQUEST_TO_APPLICATION == 0) + +/**@brief Function for responding on ECHO REQUEST message. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_ip_header Pointer to IPv6 Header. + * @param[in] p_icmp_header Pointer to ICMPv6 header. + * @param[in] p_packet Pointer to packet buffer. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static void echo_reply_send(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + iot_pbuffer_t * p_packet) +{ + uint32_t err_code; + uint16_t checksum; + iot_pbuffer_t * p_pbuffer; + iot_pbuffer_alloc_param_t pbuff_param; + + // Headers of new packet. + ipv6_header_t * p_reply_ip_header; + icmp6_header_t * p_reply_icmp_header; + + ICMP6_TRC("Sending reply on Echo Request."); + + // Requesting buffer for reply + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = ICMP6_PACKET_TYPE; + pbuff_param.length = p_packet->length; + + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + if (err_code == NRF_SUCCESS) + { + p_reply_ip_header = (ipv6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_reply_icmp_header = (icmp6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + + // Change ICMP header. + p_reply_icmp_header->type = ICMP6_TYPE_ECHO_REPLY; + p_reply_icmp_header->code = 0; + p_reply_icmp_header->checksum = 0; + + // IPv6 Header initialization. + icmp_ip_header(p_reply_ip_header, IPV6_DEFAULT_HOP_LIMIT); + + p_reply_ip_header->destaddr = p_ip_header->srcaddr; + p_reply_ip_header->length = HTONS(p_pbuffer->length + ICMP6_HEADER_SIZE); + + if (IPV6_ADDRESS_IS_MULTICAST(&p_ip_header->destaddr)) + { + IPV6_CREATE_LINK_LOCAL_FROM_EUI64(&p_reply_ip_header->srcaddr, + p_interface->local_addr.identifier); + } + else + { + p_reply_ip_header->srcaddr = p_ip_header->destaddr; + } + + // Set echo reply parameters. + p_reply_icmp_header->sp.echo.id = p_icmp_header->sp.echo.id; + p_reply_icmp_header->sp.echo.sequence = p_icmp_header->sp.echo.sequence; + + // Copy user data. + memcpy(p_pbuffer->p_payload, + p_packet->p_payload, + p_packet->length); + + // Calculate checksum. + checksum = p_pbuffer->length + ICMP6_HEADER_SIZE + IPV6_NEXT_HEADER_ICMP6; + + ipv6_checksum_calculate(p_reply_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_reply_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_pbuffer->p_payload - ICMP6_HEADER_SIZE, + p_pbuffer->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + p_reply_icmp_header->checksum = HTONS((~checksum)); + + p_pbuffer->p_payload -= ICMP6_OFFSET; + p_pbuffer->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_pbuffer); + + if (err_code != NRF_SUCCESS) + { + ICMP6_ERR("Cannot send packet buffer!"); + } + } + else + { + ICMP6_ERR("Failed to allocate packet buffer!"); + } +} +#endif + + +/**@brief Function for responding on Neighbor Advertisement message. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_ip_header Pointer to IPv6 Header. + * @param[in] p_icmp_header Pointer to ICMPv6 header. + * @param[in] p_target_addr Pointer to the IPv6 address. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static uint32_t na_send(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + ipv6_addr_t * p_target_addr) +{ + uint32_t err_code; + uint16_t checksum; + iot_pbuffer_t * p_pbuffer; + iot_pbuffer_alloc_param_t pbuff_param; + + // Headers of new packet. + ipv6_header_t * p_reply_ip_header; + icmp6_header_t * p_reply_icmp_header; + icmp6_na_header_t * p_reply_na_header; + nd_option_tllao_t * p_reply_opt_tllao_header; + + ICMP6_TRC("Sending reply on Neighbor Solocitation."); + + // Requesting buffer for reply + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = ICMP6_PACKET_TYPE; + pbuff_param.length = ND_NA_HEADER_SIZE + ND_OPT_TLLAO_SIZE - ND_PAYLOAD_ADJUST_OFFSET; + + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + if (err_code == NRF_SUCCESS) + { + p_reply_ip_header = (ipv6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_reply_icmp_header = (icmp6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + + p_pbuffer->p_payload -= ND_PAYLOAD_ADJUST_OFFSET; + + p_reply_na_header = (icmp6_na_header_t *)(p_pbuffer->p_payload); + p_reply_opt_tllao_header = (nd_option_tllao_t *)(p_pbuffer->p_payload + ND_NA_HEADER_SIZE); + + p_pbuffer->p_payload += ND_PAYLOAD_ADJUST_OFFSET; + + // Change ICMP header. + p_reply_icmp_header->type = ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT; + p_reply_icmp_header->code = 0; + p_reply_icmp_header->checksum = 0; + + // IPv6 Header initialization. + icmp_ip_header(p_reply_ip_header, ND_HOP_LIMIT); + + p_reply_ip_header->srcaddr = *p_target_addr; + p_reply_ip_header->destaddr = p_ip_header->srcaddr; + p_reply_ip_header->length = HTONS(p_pbuffer->length + ICMP6_HEADER_SIZE); + + p_reply_na_header->flags = ND_NA_S_FLAG | ND_NA_O_FLAG ; + p_reply_na_header->reserved = 0; + p_reply_na_header->target_addr = *p_target_addr; + + // Add TLLAO option. + add_tllao_opt(p_interface, p_reply_opt_tllao_header); + + // Calculate checksum. + checksum = p_pbuffer->length + ICMP6_HEADER_SIZE + IPV6_NEXT_HEADER_ICMP6; + + ipv6_checksum_calculate(p_reply_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_reply_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_pbuffer->p_payload - ICMP6_HEADER_SIZE, + p_pbuffer->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + p_reply_icmp_header->checksum = HTONS((~checksum)); + + p_pbuffer->p_payload -= ICMP6_OFFSET; + p_pbuffer->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_pbuffer); + + if (err_code != NRF_SUCCESS) + { + ICMP6_ERR("Cannot send packet buffer!"); + } + } + else + { + ICMP6_ERR("Failed to allocate packet buffer!\r\n"); + } + + return err_code; +} + + +/**@brief Function for parsing Neighbor Solicitation message. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_ip_header Pointer to IPv6 Header. + * @param[in] p_icmp_header Pointer to ICMPv6 header. + * @param[in] p_packet Pointer to packet buffer. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static uint32_t ns_input(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + iot_pbuffer_t * p_packet) +{ + uint32_t err_code = NRF_SUCCESS; + + // Read target address. + icmp6_ns_header_t * p_ns_header = (icmp6_ns_header_t *)p_packet->p_payload; + + if (ipv6_address_check(p_interface, &p_ns_header->target_addr) == NRF_SUCCESS) + { + err_code = na_send(p_interface, p_ip_header, p_icmp_header, &p_ns_header->target_addr); + } + + return err_code; +} + + +/**@brief Function for parsing Router Advertisement message. + * Because stack gives all control to application, internal RA parsing take care + * only on Context Identifier. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_ip_header Pointer to IPv6 Header. + * @param[in] p_icmp_header Pointer to ICMPv6 header. + * @param[in] p_packet Pointer to packet buffer. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static uint32_t ra_input(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + iot_pbuffer_t * p_packet) +{ + uint32_t err_code; + iot_context_t context; + iot_context_t * p_context; + uint16_t curr_opt_offset = ND_RA_HEADER_SIZE; + nd_option_t * p_opt = NULL; + nd_option_6co_t * p_6co = NULL; + nd_option_pio_t * p_pio = NULL; + + if (!IPV6_ADDRESS_IS_LINK_LOCAL(&p_ip_header->srcaddr)) + { + return ICMP6_INVALID_PACKET_DATA; + } + + // Read all option we get. + while (curr_opt_offset < p_packet->length) + { + p_opt = (nd_option_t *)(p_packet->p_payload + curr_opt_offset); + + if (p_opt->length == 0) + { + ICMP6_ERR("Invalid zero length option!"); + return ICMP6_INVALID_PACKET_DATA; + } + + ICMP6_TRC("Option type = 0x%02x!", p_opt->type); + + // Searching for handling options. + switch (p_opt->type) + { + case ND_OPT_TYPE_PIO: + { + p_pio = (nd_option_pio_t *)p_opt; + + if (p_pio->prefix_length != 0 && + (p_pio->flags & ND_OPT_PIO_A_MASK) && + !(p_pio->flags & ND_OPT_PIO_L_MASK)) + { + // Ignore Link-Local address + if (IPV6_ADDRESS_IS_LINK_LOCAL(&p_pio->prefix)) + { + ICMP6_ERR("Ignore Link-Local prefix!"); + break; + } + + // For now address is automatically set as a preferred. + ipv6_addr_conf_t temp_address; + + // Set IPv6 EUI-64 + IPV6_CREATE_LINK_LOCAL_FROM_EUI64(&temp_address.addr, + p_interface->local_addr.identifier); + + // Add prefix + IPV6_ADDRESS_PREFIX_SET(temp_address.addr.u8, + p_pio->prefix.u8, + p_pio->prefix_length); + + if (p_pio->valid_lifetime != 0) + { + temp_address.state = IPV6_ADDR_STATE_PREFERRED; + + err_code = ipv6_address_set(p_interface, &temp_address); + + if (err_code != NRF_SUCCESS) + { + ICMP6_ERR("Cannot add new address! Address table full!"); + } + } + else + { + err_code = ipv6_address_remove(p_interface, &temp_address.addr); + + if (err_code != NRF_SUCCESS) + { + ICMP6_ERR("Cannot remove address!"); + } + } + } + else + { + ICMP6_ERR("Prefix option has incorrect parameters!"); + return ICMP6_INVALID_PACKET_DATA; + } + + break; + } + case ND_OPT_TYPE_6CO: + { + p_6co = (nd_option_6co_t *)p_opt; + + memset(context.prefix.u8, 0, IPV6_ADDR_SIZE); + + context.prefix = p_6co->context; + context.prefix_len = p_6co->context_length; + context.context_id = (p_6co->CID_C & ND_OPT_6CO_CID_MASK) >> + ND_OPT_6CO_CID_POS; + context.compression_flag = (p_6co->CID_C & ND_OPT_6CO_C_MASK) >> + ND_OPT_6CO_C_POS; + + if (p_6co->valid_lifetime == 0) + { + err_code = iot_context_manager_get_by_cid(p_interface, + context.context_id, + &p_context); + + if (err_code == NRF_SUCCESS) + { + err_code = iot_context_manager_remove(p_interface, p_context); + + if (err_code == NRF_SUCCESS) + { + ICMP6_TRC("Removed context! CID = 0x%02x", context.context_id); + } + } + + } + else + { + err_code = iot_context_manager_update(p_interface, &context); + + if (err_code == NRF_SUCCESS) + { + ICMP6_TRC("New context added! CID = 0x%02x", context.context_id); + } + } + + break; + } + } + + // Increment current offset option. + curr_opt_offset += 8 * p_opt->length; + } + + return NRF_SUCCESS; +} + +/**@brief Function for notifying application of the ICMPv6 received packet. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_ip_header Pointer to IPv6 Header. + * @param[in] p_icmp_header Pointer to ICMPv6 header. + * @param[in] p_packet Pointer to packet buffer. + * + * @return NRF_SUCCESS after successful processing, error otherwise. + */ +static uint32_t ndisc_input(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + iot_pbuffer_t * p_packet) +{ + uint32_t process_result; + + switch (p_icmp_header->type) + { + case ICMP6_TYPE_ROUTER_SOLICITATION: + ICMP6_ERR("Got unsupported Router Solicitation message."); + process_result = ICMP6_UNHANDLED_PACKET_TYPE; + break; + + case ICMP6_TYPE_ROUTER_ADVERTISEMENT: + ICMP6_TRC("Got Router Advertisement message."); + process_result = ra_input(p_interface, p_ip_header, p_icmp_header, p_packet); + break; + + case ICMP6_TYPE_NEIGHBOR_SOLICITATION: + ICMP6_TRC("Got Neighbour Solicitation message."); + process_result = ns_input(p_interface, p_ip_header, p_icmp_header, p_packet); + break; + + case ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT: + ICMP6_TRC("Got Neighbour Advertisement message."); + process_result = NRF_SUCCESS; + break; + + default: + process_result = ICMP6_UNHANDLED_PACKET_TYPE; + break; + } + + return process_result; +} + +uint32_t icmp6_error_message(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr, + const icmp6_error_message_param_t * p_param) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_src_addr); + NULL_PARAM_CHECK(p_dest_addr); + NULL_PARAM_CHECK(p_param); + NULL_PARAM_CHECK(p_param->p_packet); + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + iot_pbuffer_t * p_pbuffer; + ipv6_header_t * p_ip_header; + icmp6_header_t * p_icmp_header; + iot_pbuffer_alloc_param_t pbuff_param; + uint16_t checksum; + uint32_t err_code = NRF_SUCCESS; + const uint32_t error_packet_length = + (MIN(p_param->packet_len, + ICMP6_ERROR_MESSAGE_MAX_SIZE - ICMP6_HEADER_SIZE)); + + // Requesting buffer for error message. + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = ICMP6_PACKET_TYPE; + pbuff_param.length = error_packet_length; + + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + if (err_code == NRF_SUCCESS) + { + p_ip_header = (ipv6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_icmp_header = (icmp6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + + // Change ICMP header. + p_icmp_header->type = p_param->type; + p_icmp_header->code = p_param->code; + p_icmp_header->checksum = 0; + + switch (p_param->type) + { + case ICMP6_TYPE_PACKET_TOO_LONG: + { + p_icmp_header->sp.mtu = HTONL(p_param->error_field.mtu); + break; + } + case ICMP6_TYPE_PARAMETER_PROBLEM: + { + p_icmp_header->sp.offset = HTONL(p_param->error_field.offset); + break; + } + default: + { + p_icmp_header->sp.unused = 0; + break; + } + } + + // IPv6 Header initialization. + icmp_ip_header(p_ip_header, IPV6_DEFAULT_HOP_LIMIT); + + p_ip_header->srcaddr = *p_src_addr; + p_ip_header->destaddr = *p_dest_addr; + p_ip_header->length = HTONS(p_pbuffer->length + ICMP6_HEADER_SIZE); + + memcpy(p_pbuffer->p_payload, p_param->p_packet, error_packet_length); + + // Calculate checksum. + checksum = error_packet_length + IPV6_NEXT_HEADER_ICMP6; + + ipv6_checksum_calculate(p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_pbuffer->p_payload - ICMP6_HEADER_SIZE, + p_pbuffer->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + // Update checksum in the packet. + p_icmp_header->checksum = HTONS((~checksum)); + + p_pbuffer->p_payload -= ICMP6_OFFSET; + p_pbuffer->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_pbuffer); + } + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t icmp6_echo_request(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr, + iot_pbuffer_t * p_request) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_src_addr); + NULL_PARAM_CHECK(p_dest_addr); + NULL_PARAM_CHECK(p_request); + PACKET_TYPE_CHECK(p_request); + + uint32_t err_code = NRF_SUCCESS; + uint16_t checksum; + ipv6_header_t * p_ip_header; + icmp6_header_t * p_icmp_header; + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + // Headers of IPv6 packet. + p_ip_header = (ipv6_header_t *)(p_request->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_icmp_header = (icmp6_header_t *)(p_request->p_payload - ICMP6_HEADER_SIZE); + + // Change ICMP header. + p_icmp_header->type = ICMP6_TYPE_ECHO_REQUEST; + p_icmp_header->code = 0; + p_icmp_header->checksum = 0; + + // IPv6 Header initialization. + icmp_ip_header(p_ip_header, IPV6_DEFAULT_HOP_LIMIT); + + p_ip_header->srcaddr = *p_src_addr; + p_ip_header->destaddr = *p_dest_addr; + p_ip_header->length = HTONS(p_request->length + ICMP6_HEADER_SIZE); + + // Set echo reply parameters. + p_icmp_header->sp.echo.id = 0; + p_icmp_header->sp.echo.sequence = HTONS(m_sequence_number); + + // Calculate checksum. + checksum = p_request->length + ICMP6_HEADER_SIZE + IPV6_NEXT_HEADER_ICMP6; + + ipv6_checksum_calculate(p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_request->p_payload - ICMP6_HEADER_SIZE, + p_request->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + p_icmp_header->checksum = HTONS((~checksum)); + + m_sequence_number++; + p_request->p_payload -= ICMP6_OFFSET; + p_request->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_request); + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t icmp6_rs_send(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_src_addr); + NULL_PARAM_CHECK(p_dest_addr); + + uint32_t err_code = NRF_SUCCESS; + uint16_t checksum; + iot_pbuffer_t * p_pbuffer; + iot_pbuffer_alloc_param_t pbuff_param; + + // IPv6 Headers. + ipv6_header_t * p_ip_header; + icmp6_header_t * p_icmp_header; + icmp6_rs_header_t * p_rs_header; + nd_option_sllao_t * p_sllao_opt; + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + // Requesting buffer for RS message + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = ICMP6_PACKET_TYPE; + pbuff_param.length = ND_OPT_SLLAO_SIZE; + + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + + if (err_code == NRF_SUCCESS) + { + p_ip_header = (ipv6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_icmp_header = (icmp6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + p_rs_header = (icmp6_rs_header_t *)(&p_icmp_header->sp.unused); + p_sllao_opt = (nd_option_sllao_t *)(p_pbuffer->p_payload); + + // Change ICMP header. + p_icmp_header->type = ICMP6_TYPE_ROUTER_SOLICITATION; + p_icmp_header->code = 0; + p_icmp_header->checksum = 0; + + // IPv6 Header initialization. + icmp_ip_header(p_ip_header, ND_HOP_LIMIT); + + p_ip_header->srcaddr = *p_src_addr; + p_ip_header->destaddr = *p_dest_addr; + p_ip_header->length = HTONS(p_pbuffer->length + ICMP6_HEADER_SIZE); + + // Set Router Solicitation parameter. + p_rs_header->reserved = 0; + + // Add SLLAO option. + add_sllao_opt(p_interface, p_sllao_opt); + + // Calculate checksum. + checksum = p_pbuffer->length + ICMP6_HEADER_SIZE + IPV6_NEXT_HEADER_ICMP6; + + ipv6_checksum_calculate(p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_pbuffer->p_payload - ICMP6_HEADER_SIZE, + p_pbuffer->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + p_icmp_header->checksum = HTONS((~checksum)); + + p_pbuffer->p_payload -= ICMP6_OFFSET; + p_pbuffer->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_pbuffer); + } + else + { + ICMP6_ERR("Failed to allocate packet buffer!"); + } + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return err_code; +} + +uint32_t icmp6_ns_send(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr, + const icmp6_ns_param_t * p_param) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_src_addr); + NULL_PARAM_CHECK(p_dest_addr); + NULL_PARAM_CHECK(p_param); + + uint32_t err_code = NRF_SUCCESS; + uint16_t aro_size = 0; + uint16_t checksum; + iot_pbuffer_t * p_pbuffer; + iot_pbuffer_alloc_param_t pbuff_param; + + // IPv6 Headers. + ipv6_header_t * p_ip_header; + icmp6_header_t * p_icmp_header; + icmp6_ns_header_t * p_ns_header; + nd_option_sllao_t * p_sllao_opt; + nd_option_aro_t * p_aro_opt; + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + if (p_param->add_aro) + { + aro_size = ND_OPT_ARO_SIZE; + } + + // Requesting buffer for NS message + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = ICMP6_PACKET_TYPE; + pbuff_param.length = ND_NS_HEADER_SIZE + ND_OPT_SLLAO_SIZE + \ + aro_size - ND_PAYLOAD_ADJUST_OFFSET; + + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + + if (err_code == NRF_SUCCESS) + { + p_ip_header = (ipv6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE - + IPV6_IP_HEADER_SIZE); + p_icmp_header = (icmp6_header_t *)(p_pbuffer->p_payload - ICMP6_HEADER_SIZE); + p_pbuffer->p_payload -= ND_PAYLOAD_ADJUST_OFFSET; + p_ns_header = (icmp6_ns_header_t *)(p_pbuffer->p_payload); + p_sllao_opt = (nd_option_sllao_t *)(p_pbuffer->p_payload + ND_NS_HEADER_SIZE); + p_aro_opt = (nd_option_aro_t *)(p_pbuffer->p_payload + ND_NS_HEADER_SIZE + + ND_OPT_SLLAO_SIZE); + + p_pbuffer->p_payload += ND_PAYLOAD_ADJUST_OFFSET; + + // Change ICMP header. + p_icmp_header->type = ICMP6_TYPE_NEIGHBOR_SOLICITATION; + p_icmp_header->code = 0; + p_icmp_header->checksum = 0; + + // IPv6 Header initialization. + icmp_ip_header(p_ip_header, ND_HOP_LIMIT); + + p_ip_header->srcaddr = *p_src_addr; + p_ip_header->destaddr = *p_dest_addr; + p_ip_header->length = HTONS(p_pbuffer->length + ICMP6_HEADER_SIZE); + + // Set Neighbour Solicitation parameter. + p_ns_header->reserved = 0; + p_ns_header->target_addr = p_param->target_addr; + + // Add SLLAO option. + add_sllao_opt(p_interface, p_sllao_opt); + + if (p_param->add_aro) + { + add_aro_opt(p_interface, p_aro_opt, p_param->aro_lifetime); + } + + // Calculate checksum. + checksum = p_pbuffer->length + ICMP6_HEADER_SIZE + IPV6_NEXT_HEADER_ICMP6; + + ipv6_checksum_calculate(p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_pbuffer->p_payload - ICMP6_HEADER_SIZE, + p_pbuffer->length + ICMP6_HEADER_SIZE, + &checksum, + false); + + p_icmp_header->checksum = HTONS((~checksum)); + + p_pbuffer->p_payload -= ICMP6_OFFSET; + p_pbuffer->length += ICMP6_OFFSET; + + // Send IPv6 packet. + err_code = ipv6_send(p_interface, p_pbuffer); + } + else + { + ICMP6_ERR("Failed to allocate packet buffer!"); + } + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t icmp6_receive_register(icmp6_receive_callback_t cb) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(cb); + UNUSED_VARIABLE(m_event_handler); + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + // Store application event handler. + m_event_handler = cb; + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t icmp6_init(void) +{ + SDK_MUTEX_INIT(m_icmp6_mutex); + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + // Set application event handler. + m_event_handler = NULL; + + // Indicate initialization of module. + m_initialization_state = true; + + ICMP6_EXIT(); + + ICMP6_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t icmp6_input(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + iot_pbuffer_t * p_packet) +{ + VERIFY_MODULE_IS_INITIALIZED(); + + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_ip_header); + NULL_PARAM_CHECK(p_packet); + + uint16_t checksum; + uint32_t process_result = NRF_SUCCESS; + bool is_ndisc = false; + icmp6_header_t * p_icmp_header = (icmp6_header_t *)p_packet->p_payload; + uint32_t err_code = NRF_SUCCESS; + + ICMP6_MUTEX_LOCK(); + + ICMP6_ENTRY(); + + if (p_packet->length < ICMP6_HEADER_SIZE || p_ip_header->length < ICMP6_HEADER_SIZE) + { + ICMP6_ERR("Received malformed packet, which has 0x%08lX bytes.", p_packet->length); + process_result = ICMP6_MALFORMED_PACKET; + } + else + { + // Check checksum of packet. + checksum = p_packet->length + IPV6_NEXT_HEADER_ICMP6; + + ipv6_checksum_calculate(p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_packet->p_payload, p_packet->length, &checksum, false); + checksum = (uint16_t)~checksum; + + // Change pbuffer type. + p_packet->type = ICMP6_PACKET_TYPE; + p_packet->p_payload = p_packet->p_payload + ICMP6_HEADER_SIZE; + p_packet->length -= ICMP6_HEADER_SIZE; + + if (checksum != 0) + { + ICMP6_ERR("Bad checksum detected. Got 0x%08x but expected 0x%08x, 0x%08lX", + NTOHS(p_icmp_header->checksum), checksum, p_packet->length); + process_result = ICMP6_BAD_CHECKSUM; + } + else + { + switch (p_icmp_header->type) + { + case ICMP6_TYPE_DESTINATION_UNREACHABLE: + case ICMP6_TYPE_PACKET_TOO_LONG: + case ICMP6_TYPE_TIME_EXCEED: + case ICMP6_TYPE_PARAMETER_PROBLEM: + { + ICMP6_TRC("Got ICMPv6 error message with type = 0x%08x", + p_icmp_header->type); + p_icmp_header->sp.unused = NTOHL(p_icmp_header->sp.unused); + break; + } + case ICMP6_TYPE_ECHO_REQUEST: + case ICMP6_TYPE_ECHO_REPLY: + { + ICMP6_TRC("Got ICMPv6 Echo message with type = 0x%x.", p_icmp_header->type); + ICMP6_TRC("From IPv6 Address:"); + ICMP6_DUMP(p_ip_header->srcaddr.u32, IPV6_ADDR_SIZE); + ICMP6_TRC("Identifier: 0x%04x, Sequence Number: 0x%04x", + NTOHS(p_icmp_header->sp.echo.id), + NTOHS(p_icmp_header->sp.echo.sequence)); + break; + } + case ICMP6_TYPE_ROUTER_SOLICITATION: + case ICMP6_TYPE_ROUTER_ADVERTISEMENT: + case ICMP6_TYPE_NEIGHBOR_SOLICITATION: + case ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT: + { + p_packet->p_payload = p_packet->p_payload - ND_PAYLOAD_ADJUST_OFFSET; + p_packet->length += ND_PAYLOAD_ADJUST_OFFSET; + process_result = ndisc_input(p_interface, + p_ip_header, + p_icmp_header, + p_packet); + p_packet->p_payload = p_packet->p_payload + ND_PAYLOAD_ADJUST_OFFSET; + p_packet->length -= ND_PAYLOAD_ADJUST_OFFSET; + is_ndisc = true; + break; + } + default: + process_result = ICMP6_UNHANDLED_PACKET_TYPE; + break; + } + +#if (ICMP6_ENABLE_HANDLE_ECHO_REQUEST_TO_APPLICATION == 0) + if (p_icmp_header->type == ICMP6_TYPE_ECHO_REQUEST) + { + echo_reply_send(p_interface, p_ip_header, p_icmp_header, p_packet); + } +#endif + } + } + +#if (ICMP6_ENABLE_ALL_MESSAGES_TO_APPLICATION == 1) + err_code = app_notify_icmp_data(p_interface, p_packet, process_result); +#elif (ICMP6_ENABLE_ND6_MESSAGES_TO_APPLICATION == 1) + if (is_ndisc) + { + err_code = app_notify_icmp_data(p_interface, p_packet, process_result); + } +#endif + + ICMP6_EXIT(); + + UNUSED_VARIABLE(is_ndisc); + UNUSED_VARIABLE(process_result); + + ICMP6_MUTEX_UNLOCK(); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.h new file mode 100644 index 0000000..7ce517b --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/icmp6/icmp6.h @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @cond */ + /** @file icmp6.h + * + * @defgroup iot_icmp6 ICMP Module Header. + * @ingroup iot_sdk + * @{ + * @brief Internet Control Message Protocol module header defining interface between the ICMP6 + * module and other IP stack layers. This interface is internal to the IPv6 stack and not + * available to the application. The application shall not explicitly use this interface. + * + */ + +#ifndef ICMP6_H__ +#define ICMP6_H__ + +#include "sdk_config.h" +#include "sdk_common.h" +#include "ipv6_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Module initialization function called from ipv6_init(). This shall not be called by the + * application explicitly. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t icmp6_init(void); + + +/** + * @brief Function to feed incoming ICMP packets to the module. To be called by the IPv6 + * stack only and never by the application. + * + * @param[in] p_interface Identifies network interface on which the packet is received. + * @param[in] p_ip_header IP Header of the ICMP packet being fed to the module. + * @param[in] p_packet ICMP packet being notified to the module. p_packet->p_payload points the + * IPv6 payload and p_packet->length indicates total length of the payload. + * + * @note This routine is called by the stack with next header field value is set to transport + * protocol value of 58. + * + * @retval NRF_SUCCESS on successful handling of the packet, else an error code indicating reason + * for failure. + */ +uint32_t icmp6_input(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + iot_pbuffer_t * p_packet); + + +#ifdef __cplusplus +} +#endif + +#endif //ICMP6_H__ + +/**@} */ + +/** @endcond */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/dns6_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/dns6_api.h new file mode 100644 index 0000000..0f6ecd9 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/dns6_api.h @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file dns6_api.h + * + * @defgroup iot_dns6 DNS Application Interface for Nordic's IPv6 stack + * @ingroup iot_sdk_stack + * @{ + * @brief Domain Name System module provides implementation of DNS6 service. + * + */ + +#ifndef DNS6_H__ +#define DNS6_H__ + +#include "sdk_config.h" +#include "sdk_common.h" +#include "ipv6_api.h" +#include "iot_timer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief DNS Server parameter. */ +typedef struct +{ + ipv6_addr_t addr; /**< The IPv6 address of the DNS Server. */ + uint16_t port; /**< The default UDP port of the DNS Server. */ +} dns6_server_param_t; + + +/**@brief Initialization parameters type. */ +typedef struct +{ + uint16_t local_src_port; /**< The local UDP port for reception the DNS responses. */ + dns6_server_param_t dns_server; /**< Parameters of the DNS Server. */ +} dns6_init_t; + + +/** + * @brief DNS event receive callback. + * + * @details API used to notify the application of DNS Response on specific hostname or of an error + * during resolving process. The process_result parameter indicates whether the DNS module + * was successfully processed. If the received DNS Response is malformed in a way that + * allow to assign response with specific callback (e.g. timeout occurs or hostname is not + * found), information about error is still notified to the application. The application + * should check process_result and number of IPv6 address before reading them. + * + * @param[in] process_result Notifies the application if the DNS module was processed successfully + * or if an error occurred, for example DNS server is unreachable. + * @param[in] p_hostname Identifies hostname (URL string) that was requested to bee resolved, + * e.g. "example.com". + * @param[in] p_addr Pointer to the IPv6 addresses being resolved for given hostname. In + * case addr_count variable is 0, p_addr gets NULL value and should not + * be used. + * @param[in] addr_count Number of IPv6 addresses being successfully resolved. + * + * @retval None. + */ +typedef void (* dns6_evt_handler_t) (uint32_t process_result, + const char * p_hostname, + ipv6_addr_t * p_addr, + uint16_t addr_count); + +/** + * @brief Function for initializing DNS6 module. + * + * @param[in] p_dns_init Initialization structure for DNS client. Should not be NULL. + * + * @note DNS protocol module uses UDP transport layer, therefore one UDP socket is allocated + * inside function and uses for further communication. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t dns6_init(const dns6_init_t * p_dns_init); + + +/** + * @brief Function for uninitializing DNS6 module. + * + * @note Apart of DNS specific functionality, function frees previously allocated UDP socket. + * Function removes any pending queries from the sending queue, so registered user + * callbacks will not be executed. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t dns6_uninit(void); + + +/** + * @brief Function for querying given URL string to DNS server, in order to get IPv6 address of + * given hostname. + * + * @param[in] p_hostname Identifies hostname (URL string) to be find, e.g. "example.com". Should + * not be NULL. + * @param[in] evt_handler Callback that is called once response is received, timeout occurred or + * internal error was detected. Should not be NULL. + * + * @note Function sends DNS Query to DNS Server to obtain all AAAA records (with IPv6 address) + * assigned to given hostname. In case DNS Server replies with more that one AAAA records + * DNS module call user defined evt_handler with addr_count indicates number of addresses. + * + * @retval NRF_SUCCESS on successful execution of procedure. + * @retval IOT_DNS6_ERR_BASE | NRF_ERROR_NO_MEM if there is no place in pending queries' queue. + * @retval IOT_PBUFFER_ERR_BASE | NRF_ERROR_NO_MEM if there is no memory for hostname allocation. + * @retval NRF_ERROR_MEMORY_MANAGER_ERR_BASE | NRF_ERROR_NO_MEM if there is no memory for packet allocation. + * @retval UDP_INTERFACE_NOT_READY if interface is not ready for sending packets e.g. interface is + * down. + * @retval Other errors indicates reason of failure. + */ +uint32_t dns6_query(const char * p_hostname, dns6_evt_handler_t evt_handler); + + +/**@brief Function for performing retransmissions of DNS queries. + * + * @note DNS module implements the retransmission mechanism by invoking this function periodically. + * So that method has to be added to IoT Timer client list and has to be called with minimum of + * DNS6_RETRANSMISSION_INTERVAL resolution. + * + * @param[in] wall_clock_value The value of the wall clock that triggered the callback. + * + * @retval None. + */ +void dns6_timeout_process(iot_timer_time_in_ms_t wall_clock_value); + + +#ifdef __cplusplus +} +#endif + +#endif //DNS6_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/icmp6_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/icmp6_api.h new file mode 100644 index 0000000..b13b4b6 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/icmp6_api.h @@ -0,0 +1,253 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file icmp6_api.h + * + * @defgroup iot_icmp6 ICMP6 Application Interface for Nordic's IPv6 stack + * @ingroup iot_sdk_stack + * @{ + * @brief Nordic Internet Control Message Protocol Application Interface for Nordic's IPv6 stack. + * + * @details This module provides basic features related to ICMPv6 support. + */ + +#ifndef ICMP6_API_H__ +#define ICMP6_API_H__ + +#include "sdk_config.h" +#include "sdk_common.h" +#include "iot_defines.h" +#include "ipv6_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICMP6_ECHO_REQUEST_PAYLOAD_OFFSET 8 /**< Offset of echo request payload from ICMPv6 header. */ + + +/**@defgroup icmp6_code ICMPv6 codes per message type as defined in RFC 4443. + * @ingroup iot_icmp6 + * @{ + */ +#define ICMP6_DU_CODE_NO_ROUTE_TO_DESTINATION 0 /**< Code for Destination Unreachable Message when there is no route to destination. */ +#define ICMP6_DU_CODE_ADMINISTRATIVELY_PROHIBITED 1 /**< Code for Destination Unreachable Message when the communication to destination administratively prohibited. */ +#define ICMP6_DU_CODE_BEYOND_SCOPE_OF_SOURCE 2 /**< Code for Destination Unreachable Message when the destination is beyond the scope of source address. */ +#define ICMP6_DU_CODE_ADDRESS_UNREACHABLE 3 /**< Code for Destination Unreachable Message when the destination address is unreachable. */ +#define ICMP6_DU_CODE_PORT_UNREACHABLE 4 /**< Code for Destination Unreachable Message when the destination port is unreachable. */ +#define ICMP6_DU_CODE_FAILED_INGRESS_EGRESS_POLICY 5 /**< Code for Destination Unreachable Message when the source address failed on ingress/egress policy. */ +#define ICMP6_DU_CODE_REJECT_ROUTE_TO_DESTINATION 6 /**< Code for Destination Unreachable Message when the route to destination is rejected. */ + +#define ICMP6_TE_CODE_EXCEEDED_HOP_LIMIT_TRANSIT 0 /**< Code for Time Exceeded Message when the packet received had hop limit of zero. */ +#define ICMP6_TE_CODE_FAR_TIME_EXCEEDED 1 /**< Code for Time Exceeded Message to report fragmentation and reassembly timeout. */ + +#define ICMP6_PP_CODE_INVALID_HEADER 0 /**< Code for Parameter Problem Message when the header of the incoming packet was erroneous. */ +#define ICMP6_PP_CODE_UNKNOWN_NEXT_HEADER 1 /**< Code for Parameter Problem Message when the next header of the incoming packet was unrecognized. */ +#define ICMP6_PP_CODE_UNKNOWN_IPV6_OPTION 2 /**< Code for Parameter Problem Message when the option of the incoming packet was unrecognized. */ + +#define ICMP6_ERROR_MESSAGE_INVOKING_PKT_OFFSET 8 /**< Offset in the received ICMPv6 payload where the packet (partial or complete) that invoked the error message is found. */ +/* @} */ + +/** Neighbor solicitation parameters. */ +typedef struct +{ + ipv6_addr_t target_addr; /**< The IPv6 address of the target of the solicitation. MUST NOT be a multi-cast address. */ + bool add_aro; /**< Indicates if ARO option should be added. */ + uint16_t aro_lifetime; /**< The amount of time in units of 60 seconds that the router should retain the NCE for the sender of the NS. */ +} icmp6_ns_param_t; + +/**@brief Parameters associated with error message in receive and transmit paths. */ +typedef struct +{ + uint8_t type; /**< Identifies error message type, valid values as described in RFC 4443. See @ref icmp6_error_type for possible values. */ + uint8_t code; /**< Identifies code, if any associated with the error. See \ref icmp6_code and RFC 4443 for details. */ + union /**< Additional field like MTU, pointer or unused based on message type. See RFC 4443 for more details. If unused, application may ignore this field. */ + { + uint32_t mtu; /**< MTU of next hop limit, used only with ICMP6_TYPE_PACKET_TOO_LONG type. */ + uint32_t offset; /**< Offset pointing to the parameter that resulted in the ICMP6_TYPE_PARAMETER_PROBLEM error. Used only with ICMP6_TYPE_PARAMETER_PROBLEM. */ + uint32_t unused; /**< Any other error message. Is always zero. */ + } error_field; + uint8_t * p_packet; /**< Points to the start of IPv6 packet that has resulted in the error message. */ + uint16_t packet_len; /**< Length of the packet that resulted in error. The module may truncate the packet and pack only partially the packet based on configuration of ICMP6_ERROR_MESSAGE_MAX_SIZE. */ +} icmp6_error_message_param_t; + + +/**@brief ICMPv6 data RX callback. + * + * @details Asynchronous callback used to notify the application of ICMP packets received. + * By default, the application is not notified through ICMP of messages related to ECHO + * requests or any errors. However, these notifications can easily be enabled by defining + * either the ICMP6_ENABLE_ND6_MESSAGES_TO_APPLICATION or the +* ICMP6_ENABLE_ALL_MESSAGES_TO_APPLICATION if the application should handle them. + * + * @param[in] p_interface Pointer to the IPv6 interface from where the ICMP packet was received. + * @param[in] p_ip_header Pointer to the IP header of the ICMP packet received. + * @param[in] p_icmp_header Pointer to the ICMP header of the received packet. + * @param[in] process_result Notifies the application if the ICMP packet was processed successfully or if + * an error occurred, for example, if the packet was malformed. + * @param[in] p_rx_packet Packet buffer containing the packet received. p_rx_packet->p_payload + * contains the ICMP payload. + * + * @retval A provision for the application to notify the module of whether the received packet was + * processed successfully by application. The application may take ownership of the received + * packet by returning IOT_IPV6_ERR_PENDING, in which case the application must take care to + * free it using @ref iot_pbuffer_free. + */ +typedef uint32_t (*icmp6_receive_callback_t)(iot_interface_t * p_interface, + ipv6_header_t * p_ip_header, + icmp6_header_t * p_icmp_header, + uint32_t process_result, + iot_pbuffer_t * p_rx_packet); + + +/**@brief Sends ICMPv6 Error Message as defined in RFC 4443. + * + * @details API to send messages categorized under error messages. See @ref icmp6_error_type and + * RFC 4443 for valid types. + * + * @param[in] p_interface Identifies the interface on which the procedure was requested. + * Shall not be NULL. + * @param[in] p_src_addr Source IPv6 address to be used for the request. Shall not be NULL. + * @param[in] p_dest_addr Destination IPv6 address to which the message send is requested. + * Shall not be NULL. + * @param[in] p_param Parameters describing Type, code, invoking packet information any + * additional details associated with the error message. + * + * @retval NRF_SUCCESS If the send request was successful, else, an error code indicating reason for + * failure. + */ +uint32_t icmp6_error_message(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr, + const icmp6_error_message_param_t * p_param); + + +/**@brief Sends ICMPv6 echo request as defined in RFC4443. + * + * @details API used to send an ICMPv6 echo request packet to a specific destination address. + * The user can decide how much additional data must be sent. + * + * The application calling the function should allocate a packet before, with the type set to + * ICMP6_PACKET_TYPE in the allocation parameter. + * + * The application should pack the payload at ICMP6_ECHO_REQUEST_PAYLOAD_OFFSET. ID, + * Sequence number, ICMP Code, type checksum, etc. are filled in by the module. + * + * The application shall not free the allocated packet buffer if the procedure was successful, + * to ensure that no data copies are needed when transmitting a packet. + * + * @param[in] p_interface Pointer to the IPv6 interface to send the ICMP packet. + * @param[in] p_src_addr IPv6 source address from where the echo request is sent. + * @param[in] p_dest_addr IPv6 destination address to where the echo request is sent. + * @param[in] p_request Packet buffer containing the echo request. + * + * @retval NRF_SUCCESS If the send request was successful. + */ +uint32_t icmp6_echo_request(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr, + iot_pbuffer_t * p_request); + + +/**@brief Sends router solicitation message defined in RFC6775. + * + * @details API used to send a neighbor discovery message of type Router Solicitation to a specific + * destination address. If no address is known, the user should send the message to all + * routers' address (FF02::1). + * + * The function internally tries to allocate a packet buffer. EUI-64 used in the SLLAO option is + * taken from the interface parameter defined in the @ref ipv6_init() function. + * + * @param[in] p_interface Pointer to the IPv6 interface to send the ICMP packet. + * @param[in] p_src_addr IPv6 source address from where the router solicitation message is + * sent. + * @param[in] p_dest_addr IPv6 destination address to where the router solicitation message is + * sent. + * + * @retval NRF_SUCCESS If the send request was successful. + */ +uint32_t icmp6_rs_send(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr); + + +/**@brief Sends neighbour solicitation message defined in RFC6775. + * + * @details API used to send a neighbor discovery message of type Neighbor Solicitation to a + * specific destination address. + * + * The function internally tries to allocate a packet buffer. EUI-64 used in the SLLAO and ARO + * options is taken from the interface parameter defined in the @ref ipv6_init() function. + * + * @param[in] p_interface Pointer to the IPv6 interface to send the ICMP packet. + * @param[in] p_src_addr IPv6 source address from where the neighbor solicitation message is + * sent. + * @param[in] p_dest_addr IPv6 destination address to where the neighbor solicitation message + * is sent. + * @param[in] p_ns_param Neighbor discovery parameters. + * + * @retval NRF_SUCCESS If the send request was successful. + */ +uint32_t icmp6_ns_send(const iot_interface_t * p_interface, + const ipv6_addr_t * p_src_addr, + const ipv6_addr_t * p_dest_addr, + const icmp6_ns_param_t * p_ns_param); + + +/**@brief Registers the callback function for echo reply. + * + * @details API used to register callback to indicate the ICMP echo reply packet. Could be not used. + * + * Neighbor discovery related messages are not relayed to the application by default. + * However, this can be enabled by using the ICMP6_ENABLE_ND6_MESSAGES_TO_APPLICATION + * configuration parameter. + * + * @param[in] cb Handler called when an ICMP packet is received. + * + * @retval NRF_SUCCESS If the registration was successful. + */ +uint32_t icmp6_receive_register(icmp6_receive_callback_t cb); + +#ifdef __cplusplus +} +#endif + +#endif //ICMP6_API_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/ipv6_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/ipv6_api.h new file mode 100644 index 0000000..33b8ec9 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/ipv6_api.h @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file ipv6_api.h + * + * @defgroup iot_ipv6 IPv6 Core Application Interface for Nordic's IPv6 stack + * @ingroup iot_sdk_stack + * @{ + * @brief Nordic's IPv6 stack. Currently, only a Host role is supported. + * + * @details Nordic's IPv6 stack provides minimal implementations of ICMP, UDP for a Host, and + * IPv6 Neighbor Discovery for Host. + * Router, neighbor, and prefix cache are not maintained across BLE link disconnections or + * power cycles. + */ + +#ifndef IPV6_API_H_ +#define IPV6_API_H_ + +#include <stdint.h> +#include "sdk_config.h" +#include "iot_common.h" +#include "iot_pbuffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Asynchronous event identifiers type. */ +typedef enum +{ + IPV6_EVT_INTERFACE_ADD, /**< Notification of a new IPv6 interface added. */ + IPV6_EVT_INTERFACE_DELETE, /**< Notification of IPv6 interface deleted. */ + IPV6_EVT_INTERFACE_RX_DATA /**< Notification of IPv6 data, depending on configuration. For example, IPV6_ENABLE_USNUPORTED_PROTOCOLS_TO_APPLICATION. */ +} ipv6_event_id_t; + +/**@brief IPv6 address configuration. */ +typedef struct +{ + ipv6_addr_t addr; + ipv6_addr_state_t state; +} ipv6_addr_conf_t; + +/**@brief Event parameters associated with the IPV6_EVT_INTERFACE_RX_DATA event. */ +typedef struct +{ + ipv6_header_t * p_ip_header; /**< IPv6 header of the packet. */ + iot_pbuffer_t * p_rx_packet; /**< Packet buffer contains received data. */ +} ipv6_data_rx_t; + +/**@brief Asynchronous event parameter type. */ +typedef union +{ + ipv6_data_rx_t rx_event_param; /**< Parameters notified with the received IPv6 packet. */ +} ipv6_event_param_t; + +/**@brief Asynchronous event type. */ +typedef struct +{ + ipv6_event_id_t event_id; /**< Event identifier. */ + ipv6_event_param_t event_param; /**< Event parameters. */ +} ipv6_event_t; + +/**@brief Asynchronous event notification callback type. */ +typedef void (* ipv6_evt_handler_t)(iot_interface_t * p_interface, + ipv6_event_t * p_event); + +/**@brief Initialization parameters type. */ +typedef struct +{ + eui64_t * p_eui64; /**< Global identifiers EUI-64 address of device. */ + ipv6_evt_handler_t event_handler; /**< Asynchronous event notification callback registered to receive IPv6 events. */ +} ipv6_init_t; + + +/**@brief Initializes the IPv6 stack module. + * + * @param[in] p_init Initialization parameters. + * + * @retval NRF_SUCCESS If initialization was successful. Otherwise, an error code is returned. + */ +uint32_t ipv6_init(const ipv6_init_t * p_init); + +/**@brief Sets address to specific interface. + * + * @details API used to add or update an IPv6 address on an interface. The address can have three specific + * states that determine transmitting capabilities. + * + * @param[in] p_interface The interface on which the address must be assigned. + * @param[in] p_addr IPv6 address and state to be assigned/updated. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_NO_MEM If no memory was available. + */ +uint32_t ipv6_address_set(const iot_interface_t * p_interface, + const ipv6_addr_conf_t * p_addr); + + +/**@brief Removes address from specific interface. + * + * @details API used to remove an IPv6 address from an interface. + * + * @param[in] p_interface The interface from which the address must be removed. + * @param[in] p_addr IPv6 address to remove. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_NOT_FOUND If no address was found. + */ +uint32_t ipv6_address_remove(const iot_interface_t * p_interface, + const ipv6_addr_t * p_addr); + + +/**@brief Checks if given unicast address has been registered. + * + * @param[in] p_interface The interface on which IPv6 address wil be checked. + * @param[in] p_addr IPv6 address to be checked. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_NOT_FOUND If no address was found. + */ +uint32_t ipv6_address_check(const iot_interface_t * p_interface, + const ipv6_addr_t * p_addr); + + +/**@brief Finds the best matched address and interface. + * + * @details API used to find the most suitable interface and address to a given destination address. + * + * To look only for the interface, set p_addr_r to NULL. + * + * To find the best matched address, IPV6_ADDR_STATE_PREFERRED state of address is required. + * + * @param[out] pp_interface Interface to be found. + * @param[out] p_addr_r Best matching address if procedure succeeded and this value was not NULL. + * @param[inout] p_addr_f IPv6 address for which best matching interface and/or address are requested. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_NOT_FOUND If no interface was found. + * @retval NRF_ERROR_NOT_SUPPORTED If the operation was not supported. + */ +uint32_t ipv6_address_find_best_match(iot_interface_t ** pp_interface, + ipv6_addr_t * p_addr_r, + const ipv6_addr_t * p_addr_f); + + +/**@brief Sends IPv6 packet. + * + * @details API used to send an IPv6 packet. Which interface that packet must be sent to is determined + * by analyzing the destination address. + * + * @param[in] p_interface The interface to which the packet is to be sent. + * @param[in] p_packet IPv6 packet to send. The packet should be allocated using + * @ref iot_pbuffer_allocate, to give stack control and to release + * the memory buffer. + * + * @retval NRF_SUCCESS If the send request was successful. + * @retval NRF_ERROR_NOT_FOUND If there was a failure while looking for the interface. + * @retval NRF_ERROR_INVALID_PARAM If there was an error in processing the IPv6 packet. + * @retval NRF_ERROR_NO_MEM If no memory was available in the transport + * interface. + */ +uint32_t ipv6_send(const iot_interface_t * p_interface, iot_pbuffer_t * p_packet); + +#ifdef __cplusplus +} +#endif + +#endif //IPV6_API_H_ + +/** @} */ + diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/udp_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/udp_api.h new file mode 100644 index 0000000..8efa378 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/include/udp_api.h @@ -0,0 +1,255 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file udp_api.h + * + * @defgroup iot_udp UDP Application Interface for Nordic's IPv6 stack + * @ingroup iot_sdk_stack + * @{ + * @brief Nordic User Datagram Protocol Application Interface for Nordic's IPv6 stack. + * + * @details This module provides basic features related to User Datagram Protocol (UDP) support. + */ + +#ifndef UDP_API_H__ +#define UDP_API_H__ + +#include "sdk_config.h" +#include "sdk_common.h" +#include "iot_defines.h" +#include "ipv6_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief UDP socket reference. + */ +typedef struct +{ + uint32_t socket_id; /**< UDP socket identifier. */ + void * p_app_data; /**< Pointer to application data mapped by the application to the socket. If no mapping is provided by the application using the @ref udp6_socket_app_data_set API, this pointer is NULL. */ +} udp6_socket_t; + +/** + * @brief UDP data receive callback. + * + * @details API used to notify the application of UDP packets received. If the received data is + * malformed (for example, a checksum error), the packet is still notified to the application. + * The process_result parameter indicates whether the packet was successfully processed by UDP. + * The application should check process_result before + * consuming the packet. + * + * @param[in] p_socket Reference to the socket on which the data is received. + * @param[in] p_ip6_header Pointer to the IP header of the received ICMP packet. + * @param[in] p_udp_header Pointer to the UDP header of the received packet. + * @param[in] process_result Notifies the application if the UDP packet was processed successfully success or + * if an error occurred, for example, the packet was malformed. + * @param[in] p_rx_packet Packet buffer containing the received packed. p_rx_packet->p_payload + * contains the UDP payload. + * + * @returns A provision for the application to notify the module of whether the received packet was + * processed successfully by application. The application may take ownership of the received + * packet by returning IOT_IPV6_ERR_PENDING, in which case the application must take care to + * free it using @ref iot_pbuffer_free. + */ +typedef uint32_t (* udp6_handler_t)(const udp6_socket_t * p_socket, + const ipv6_header_t * p_ip_header, + const udp6_header_t * p_udp_header, + uint32_t process_result, + iot_pbuffer_t * p_rx_packet); + + +/** + * @brief Allocates a UDP socket. + * + * @details This API should be called to be assigned a UDP socket. The maximum number of sockets that can + * be allocated using the API is determined by the define UDP6_MAX_SOCKET_COUNT. + * + * @param[out] p_socket Reference to the allocated socket is provided in the pointer if the procedure + * was successful. Should not be NULL. + * + * @retval NRF_SUCCESS If the socket was allocated successfully. Otherwise, an + * error code that indicates the reason for the failure is returned. + */ +uint32_t udp6_socket_allocate(udp6_socket_t * p_socket); + + +/** + * @brief Frees an allocated UDP socket. + * + * @details API used to free a socket allocated using @ref udp6_socket_allocate. + * + * @param[in] p_socket Handle reference to the socket. Should not be NULL. + * + * @retval NRF_SUCCESS If the socket was freed successfully. Otherwise, an + * error code that indicates the reason for the failure is returned. + * + */ +uint32_t udp6_socket_free(const udp6_socket_t * p_socket); + + +/** + * @brief Registers callback to be notified of data received on a socket. + * + * @details API to register a callback to be notified of data received on a socket. + * + * @param[in] p_socket Handle reference to the socket. Should not be NULL. + * @param[in] callback Callback being registered to receive data. Should not be NULL. + * + * @retval NRF_SUCCESS If the procedure was executed successfully. Otherwise, an + * error code that indicates the reason for the failure is returned. + * + */ +uint32_t udp6_socket_recv(const udp6_socket_t * p_socket, + const udp6_handler_t callback); + + +/** + * @brief Binds a UDP socket to a specific port and address. + * + * @details API used to bind a UDP socket to a local port and an address. + * + * @param[in] p_socket Handle reference to the socket. Should not be NULL. + * @param[in] p_src_addr Local IPv6 address to be bound on specific socket. + * @param[in] src_port Local UDP port to be bound on specific socket. + * + * @retval NRF_SUCCESS If the procedure was executed successfully. Otherwise, an + * error code that indicates the reason for the failure is returned. + * + */ +uint32_t udp6_socket_bind(const udp6_socket_t * p_socket, + const ipv6_addr_t * p_src_addr, + uint16_t src_port); + + +/** + * @brief Connects a UDP socket to aspecific port and address. + * + * @details API used to connect a UDP socket to a remote port and remote address. + * + * @param[in] p_socket Handle reference to the socket. Should not be NULL. + * @param[in] p_dest_addr IPv6 address of the remote destination. + * @param[in] dest_port Remote USP port to connect the socket to. + * + * @retval NRF_SUCCESS If the connection was established successfully. + */ +uint32_t udp6_socket_connect(const udp6_socket_t * p_socket, + const ipv6_addr_t * p_dest_addr, + uint16_t dest_port); + + +/** + * @brief Sends a UDP packet on a specific socket. + * + * @details API used to send UDP data over a UDP socket. Remote port and address must be set with + * \ref udp6_socket_connect() before using this API. + * + * Applications that call this function should allocate a packet with type + * UDP6_PACKET_TYPE (set in the allocation + * parameter) before calling the function. + * + * The application shall not free the allocated packet buffer if the procedure was + * successful, to ensure that no data copies are needed when transmitting a packet. + * + * @param[in] p_socket Handle reference to the socket. Should not be NULL. + * @param[in] p_packet Data to be transmitted on the socket. The application should allocate a packet + * buffer with type UDP6_PACKET_TYPE using \ref iot_pbuffer_allocate. + * p_packet->p_payload and p_packet->length should be appropriately + * populated by the application to contain the payload and length of the UDP + * packet, respectively. + * + * @retval NRF_SUCCESS If the procedure was executed successfully. Otherwise, an + * error code that indicates the reason for the failure is returned. + * + */ +uint32_t udp6_socket_send(const udp6_socket_t * p_socket, + iot_pbuffer_t * p_packet); + + +/** + * @brief Sends a UDP packet on a specific socket to a remote address and port. + * + * @details API used to send UDP data over a UDP socket. + * + * @param[in] p_socket Handle reference to the socket. Should not be NULL. + * @param[in] p_dest_addr IPv6 address of the remote destination. + * @param[in] dest_port Remote UDP port to which data transmission is requested. + * @param[in] p_packet Data to be transmitted on the socket. Application should allocate a + * packet buffer with type UDP6_PACKET_TYPE using \ref + * iot_pbuffer_allocate. p_packet->p_payload and p_packet->length should + * be appropriately populated by the application to contain the payload and + * length of the UDP packet, respectively. + * + * @retval NRF_SUCCESS If the procedure was executed successfully. Otherwise, an + * error code that indicates the reason for the failure is returned. + * + */ +uint32_t udp6_socket_sendto(const udp6_socket_t * p_socket, + const ipv6_addr_t * p_dest_addr, + uint16_t dest_port, + iot_pbuffer_t * p_packet); + + +/** + * @brief Sets application data for a socket. + * + * @details A utility API that allows the application to set any application specific mapping with the + * UDP Socket. The UDP module remembers the pointer provided by the application as long as the + * socket is not freed and if receive data callback is called each time as part of + * udp_socket_t. + * + * @param[in] p_socket Pointer to the socket for which the application data mapping is being set. + * A pointer to the application data should be provided by setting the + * p_socket->p_app_data field. + * + * @retval NRF_SUCCESS If the procedure was executed successfully. Otherwise, an + * error code that indicates the reason for the failure is returned. + * + */ +uint32_t udp6_socket_app_data_set(const udp6_socket_t * p_socket); + +#ifdef __cplusplus +} +#endif + +#endif //UDP_API_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/ipv6/ipv6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/ipv6/ipv6.c new file mode 100644 index 0000000..6d7b641 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/ipv6/ipv6.c @@ -0,0 +1,1088 @@ +/** + * Copyright (c) 2013 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include "ble_6lowpan.h" +#include "mem_manager.h" +#include "sdk_os.h" +#include "sdk_config.h" +#include "iot_common.h" +#include "iot_context_manager.h" +#include "ipv6_api.h" +#include "icmp6_api.h" +#include "udp_api.h" +#include "icmp6.h" +#include "udp.h" + +#if IPV6_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME ipv6 + +#define NRF_LOG_LEVEL IPV6_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR IPV6_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR IPV6_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define IPV6_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define IPV6_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define IPV6_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define IPV6_ENTRY() IPV6_TRC(">> %s", __func__) +#define IPV6_EXIT() IPV6_TRC("<< %s", __func__) + +#else // IPV6_CONFIG_LOG_ENABLED + +#define IPV6_TRC(...) /**< Disables traces. */ +#define IPV6_DUMP(...) /**< Disables dumping of octet streams. */ +#define IPV6_ERR(...) /**< Disables error logs. */ + +#define IPV6_ENTRY(...) +#define IPV6_EXIT(...) + +#endif // IPV6_CONFIG_LOG_ENABLED + +/** + * @defgroup ipv6_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case need arises to use an alternative architecture. + * @{ + */ +#define IPV6_MUTEX_LOCK() SDK_MUTEX_LOCK(m_ipv6_mutex) /**< Lock module using mutex */ +#define IPV6_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_ipv6_mutex) /**< Unlock module using mutex */ +/** @} */ + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * IPV6_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks. + * + * @{ + */ + +#if (IPV6_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Macro to check is module is initialized before requesting one of the module procedures. */ +#define VERIFY_MODULE_IS_INITIALIZED() \ + if (m_event_handler == NULL) \ + { \ + return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_IPV6_ERR_BASE); \ + } + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_IPV6_ERR_BASE); \ + } + +#else // IPV6_DISABLE_API_PARAM_CHECK + +#define VERIFY_MODULE_IS_INITIALIZED() +#define NULL_PARAM_CHECK(PARAM) + +#endif // IPV6_DISABLE_API_PARAM_CHECK +/** @} */ + +#define PBUFFER_ICMP_PAYLOAD_OFFSET IPV6_IP_HEADER_SIZE + ICMP6_HEADER_SIZE /**< ICMP payload offset. */ +#define PBUFFER_UDP_PAYLOAD_OFFSET IPV6_IP_HEADER_SIZE + UDP_HEADER_SIZE /**< UDP payload offset. */ +#define PBUFFER_OTHER_PAYLOAD_OFFSET IPV6_IP_HEADER_SIZE /**< Raw IPv6 payload offset. */ + +#define IPV6_MAX_ADDRESS_COUNT (IPV6_MAX_ADDRESS_PER_INTERFACE * IPV6_MAX_INTERFACE) /**< Maximum number of addresses. */ +#define IPV6_INVALID_ADDR_INDEX 0xFF /**< Invalid address representation. */ + +#define DEST_ADDR_OFFSET 24 /**< Offset of destination address in IPv6 packet. */ + +/**@brief Internal interface structure. */ +typedef struct +{ + iot_interface_t * p_interface; /**< Pointer to driver interface */ + uint8_t addr_range[IPV6_MAX_ADDRESS_PER_INTERFACE]; /**< Indexes to m_address_table indicating the address. If an index is IPV6_INVALID_ADDR_INDEX, it means there is no address entry. */ +} ipv6_interface_t; + +/**@brief Application Event Handler. */ +static ipv6_evt_handler_t m_event_handler = NULL; + +/**@brief Table of addresses */ +static ipv6_addr_conf_t m_address_table[IPV6_MAX_ADDRESS_COUNT]; + +/**@brief Network interfaces table. */ +static ipv6_interface_t m_interfaces[IPV6_MAX_INTERFACE]; + +/**@brief Number of network interfaces. */ +static uint32_t m_interfaces_count = 0; + +/**@brief Global address for IPv6 any. */ +ipv6_addr_t ipv6_addr_any; + +/**@brief Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ +SDK_MUTEX_DEFINE(m_ipv6_mutex) + + +/**@brief Function for finding specific address in address table. + * + * @param[in] p_addr Checked address. + * @param[out] p_index Index of address. + * + * @return NRF_SUCCESS if success, NRF_ERROR_NOT_FOUND otherwise. + */ +static uint32_t addr_find(const ipv6_addr_t * p_addr, uint32_t * p_index) +{ + uint32_t index; + uint32_t err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NOT_FOUND); + + for (index = 0; index < IPV6_MAX_ADDRESS_COUNT; index++) + { + if ((m_address_table[index].state != IPV6_ADDR_STATE_UNUSED) && + (0 == IPV6_ADDRESS_CMP(&m_address_table[index].addr, p_addr))) + { + *p_index = index; + err_code = NRF_SUCCESS; + break; + } + } + + return err_code; +} + +/**@brief Function for finding free place in address table. + * + * @param[out] p_index Index of address. + * + * @return NRF_SUCCESS if success, NRF_ERROR_NOT_FOUND otherwise. + */ +static uint32_t addr_find_free(uint32_t * p_index) +{ + uint32_t index; + uint32_t err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NO_MEM); + + for (index = 0; index < IPV6_MAX_ADDRESS_COUNT; index++) + { + if (m_address_table[index].state == IPV6_ADDR_STATE_UNUSED) + { + *p_index = index; + err_code = NRF_SUCCESS; + break; + } + } + + return err_code; +} + + +/**@brief Function for freeing an address configuration entry in m_address_table. + * + * @param[in] index Index of address. + * @param[in] check_references Indicate that before remove references should be counted. + * + * @return None. + */ +static void addr_free(uint32_t addr_index, bool check_references) +{ + uint32_t if_index; + uint32_t index; + + if (check_references) + { + for (if_index = 0; if_index < IPV6_MAX_INTERFACE; if_index++) + { + for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++) + { + if (m_interfaces[if_index].addr_range[index] == addr_index) + { + return; + } + } + } + } + + m_address_table[addr_index].state = IPV6_ADDR_STATE_UNUSED; + IPV6_ADDRESS_INITIALIZE(&m_address_table[addr_index].addr); +} + + +/**@brief Function for checking if received packet is for us. + * Currently only all-node, MLDv2 and solicited-node +* multicast addresses are accepted. + * + * @param[in] interface_id Index of the interface. + * @param[in] p_addr Checked address. + * @param[in] check_multicast Define if multicast addresses have to be checked. + * + * @return NRF_SUCCESS if packet can be processing to IPv6 multiplexer. + */ +static uint32_t addr_check(uint32_t interface_id, const ipv6_addr_t * p_addr, bool check_multicast) +{ + ipv6_addr_conf_t * p_addr_conf; + uint32_t index; + uint32_t err_code = NRF_ERROR_NOT_FOUND; + + // Check basic Multicast addresses. + if (check_multicast && (IPV6_ADDRESS_IS_MLDV2_MCAST(p_addr) || IPV6_ADDRESS_IS_ALL_NODE(p_addr))) + { + return NRF_SUCCESS; + } + + for (index = 0; m_interfaces[interface_id].addr_range[index] != IPV6_INVALID_ADDR_INDEX; index++) + { + p_addr_conf = &m_address_table[m_interfaces[interface_id].addr_range[index]]; + + if (check_multicast && IPV6_ADDRESS_IS_MULTICAST_SOLICITED_NODE(p_addr)) + { + // Solicited-node multicast address is formed by taking the low-order 24 bits of an address (unicast or anycast). + if (0 == memcmp(&p_addr_conf->addr.u8[13], &p_addr->u8[13], 3)) + { + err_code = NRF_SUCCESS; + break; + } + } + else if (0 == IPV6_ADDRESS_CMP(&p_addr_conf->addr, p_addr)) + { + err_code = NRF_SUCCESS; + break; + } + } + + return err_code; +} + + +/**@brief Function for adding/updating IPv6 address in table. + * + * @param[in] interface_id Index of interface. + * @param[in] p_addr Given address. + * + * @return NRF_SUCCESS if operation successful, NRF_ERROR_NO_MEM otherwise. + */ +static uint32_t addr_set(const iot_interface_t * p_interface, + const ipv6_addr_conf_t * p_addr) +{ + uint32_t index; + uint32_t addr_index; + uint32_t err_code; + + uint32_t interface_id = (uint32_t)p_interface->p_upper_stack; + + // Try to find address. + err_code = addr_find(&p_addr->addr, &addr_index); + + if (err_code != NRF_SUCCESS) + { + // Find first empty one. + err_code = addr_find_free(&addr_index); + } + + if (err_code == NRF_SUCCESS) + { + err_code = IOT_IPV6_ERR_ADDR_IF_MISMATCH; + + // Check if this index entry exists in the p_interface for which API is requested. + for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++) + { + if (m_interfaces[interface_id].addr_range[index] == addr_index) + { + m_address_table[index].state = p_addr->state; + + err_code = NRF_SUCCESS; + break; + } + } + + if (err_code == IOT_IPV6_ERR_ADDR_IF_MISMATCH) + { + err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NO_MEM); + + for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++) + { + if (m_interfaces[interface_id].addr_range[index] == IPV6_INVALID_ADDR_INDEX) + { + m_address_table[index].state = p_addr->state; + memcpy(&m_address_table[index].addr, p_addr, IPV6_ADDR_SIZE); + m_interfaces[interface_id].addr_range[index] = addr_index; + + err_code = NRF_SUCCESS; + break; + } + } + } + } + + return err_code; +} + + +/**@brief Function for calculating how many bits of addresses are equal. + * + * @param[in] p_addr1 Base address. + * @param[in] p_addr2 Base address. + * + * @return Number of same bits. + */ +static uint32_t addr_bit_equal(const ipv6_addr_t * p_addr1, + const ipv6_addr_t * p_addr2) +{ + uint32_t index; + uint32_t match = 0; + uint8_t temp; + uint32_t index_tab; + + for (index = 0; index < IPV6_ADDR_SIZE; index++) + { + if (p_addr1->u8[index] == p_addr2->u8[index]) + { + // Add full 8bits to match + match += 8; + } + else + { + // Operation of XOR to detect differences + temp = p_addr1->u8[index] ^ p_addr2->u8[index]; + + // Check all single bits + for (index_tab = 0; index_tab < 8; index_tab++) + { + if ((temp & 0x80) == 0) + { + // If the oldest bits matched, add one more. + match++; + + // Check next bit. + temp = temp << 1; + } + else + { + break; + } + } + + break; + } + } + + return match; +} + +/**@brief Function for searching specific network interface by given address. + * + * @param[in] p_interface Pointer to IPv6 network interface. + * @param[in] p_dest_addr IPv6 address to be matched. + * + * @return NRF_SUCCESS if operation successful, NRF_ERROR_NOT_FOUND otherwise. + */ +static uint32_t interface_find(iot_interface_t ** pp_interface, const ipv6_addr_t * p_dest_addr) +{ + // Currently only host role is implemented, though no need to match addresses. + UNUSED_VARIABLE(p_dest_addr); + + uint32_t index; + uint32_t err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NOT_FOUND); + + if (m_interfaces_count == 1) + { + for (index = 0; index < IPV6_MAX_INTERFACE; index++) + { + if (m_interfaces[index].p_interface != NULL) + { + *pp_interface = m_interfaces[index].p_interface; + err_code = NRF_SUCCESS; + break; + } + } + } + else if (m_interfaces_count == 0) + { + err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NOT_FOUND); + } + else + { + // Not supported now. + err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NOT_SUPPORTED); + } + + return err_code; +} + + +/**@brief Function for resetting specific network interface. + * + * @param[in] p_interface Pointer to IPv6 network interface. + * + * @return None. + */ +static void interface_reset(ipv6_interface_t * p_interface) +{ + uint32_t index; + uint8_t addr_index; + + p_interface->p_interface = NULL; + + for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++) + { + addr_index = p_interface->addr_range[index]; + + if (addr_index != IPV6_INVALID_ADDR_INDEX) + { + p_interface->addr_range[index] = IPV6_INVALID_ADDR_INDEX; + addr_free(index, true); + } + } +} + + +/**@brief Function for getting specific network interface by 6LoWPAN interface. + * + * @param[in] p_6lo_interface Pointer to 6LoWPAN interface. + * + * @return Pointer to internal network interface on success, otherwise NULL. + */ +static uint32_t interface_get_by_6lo(iot_interface_t * p_6lo_interface) +{ + return (uint32_t)(p_6lo_interface->p_upper_stack); +} + + +/**@brief Function for adding new 6lowpan interface to interface table. + * + * @param[in] p_6lo_interface Pointer to 6LoWPAN interface. + * @param[out] p_index Pointer to index of internal network interface. + * + * @return NRF_SUCCESS on success, otherwise NRF_ERROR_NO_MEM error. + */ +static uint32_t interface_add(iot_interface_t * p_interface, + uint32_t * p_index ) +{ + uint32_t index; + uint32_t err_code; + ipv6_addr_conf_t linklocal_addr; + + for (index = 0; index < IPV6_MAX_INTERFACE; index++) + { + if (m_interfaces[index].p_interface == NULL) + { + m_interfaces[index].p_interface = p_interface; + p_interface->p_upper_stack = (void *) index; + (*p_index) = index; + + // Add link local address. + IPV6_CREATE_LINK_LOCAL_FROM_EUI64(&linklocal_addr.addr, p_interface->local_addr.identifier); + linklocal_addr.state = IPV6_ADDR_STATE_PREFERRED; + + err_code = addr_set(p_interface, &linklocal_addr); + if (err_code != NRF_SUCCESS) + { + IPV6_ERR("Cannot add link-local address to interface!"); + } + + return NRF_SUCCESS; + } + } + + return NRF_ERROR_NO_MEM; +} + + +/**@brief Function for removing 6lowpan interface from interface table. + * + * @param[in] p_interface Pointer to internal network interface. + * + * @return None. + */ +static void interface_delete(uint32_t index) +{ + interface_reset(&m_interfaces[index]); +} + +/**@brief Function for notifying application of the new interface established. + * + * @param[in] p_interface Pointer to internal network interface. + * + * @return None. + */ +static void app_notify_interface_add(iot_interface_t * p_interface) +{ + ipv6_event_t event; + + event.event_id = IPV6_EVT_INTERFACE_ADD; + + IPV6_MUTEX_UNLOCK(); + + m_event_handler(p_interface, &event); + + IPV6_MUTEX_LOCK(); +} + + +/**@brief Function for notifying application of the interface disconnection. + * + * @param[in] p_interface Pointer to internal network interface. + * + * @return None. + */ +static void app_notify_interface_delete(iot_interface_t * p_interface) +{ + ipv6_event_t event; + + event.event_id = IPV6_EVT_INTERFACE_DELETE; + + IPV6_MUTEX_UNLOCK(); + + m_event_handler(p_interface, &event); + + IPV6_MUTEX_LOCK(); +} + + +#if (IPV6_ENABLE_USNUPORTED_PROTOCOLS_TO_APPLICATION == 1) +/**@brief Function for notifying application of the received packet (e.g. with unsupported protocol). + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_pbuffer Pointer to packet buffer. + * + * @return None. + */ +static void app_notify_rx_data(iot_interface_t * p_interface, iot_pbuffer_t * p_pbuffer) +{ + ipv6_event_t event; + + event.event_id = IPV6_EVT_INTERFACE_RX_DATA; + + // RX Event parameter. + event.event_param.rx_event_param.p_rx_packet = p_pbuffer; + event.event_param.rx_event_param.p_ip_header = (ipv6_header_t *)p_pbuffer->p_memory; + + IPV6_MUTEX_UNLOCK(); + + m_event_handler(p_interface, &event); + + IPV6_MUTEX_LOCK(); +} +#endif + + +/**@brief Function for multiplexing transport protocol to different modules. + * + * @param[in] p_interface Pointer to external interface from which packet come. + * @param[in] p_pbuffer Pointer to packet buffer. + * + * @return NRF_SUCCESS if success, otherwise an error code. + */ +static uint32_t ipv6_input(iot_interface_t * p_interface, iot_pbuffer_t * p_pbuffer) +{ + uint32_t err_code = NRF_SUCCESS; + ipv6_header_t * p_iphdr = (ipv6_header_t *)(p_pbuffer->p_payload - IPV6_IP_HEADER_SIZE); + + // Change byte order of IP header given to application. + p_iphdr->length = NTOHS(p_iphdr->length); + p_iphdr->flowlabel = NTOHS(p_iphdr->flowlabel); + + switch (p_iphdr->next_header) + { + case IPV6_NEXT_HEADER_ICMP6: + IPV6_TRC("Got ICMPv6 packet."); + + IPV6_MUTEX_UNLOCK(); + err_code = icmp6_input(p_interface, p_iphdr, p_pbuffer); + IPV6_MUTEX_LOCK(); + + break; + + case IPV6_NEXT_HEADER_UDP: + IPV6_TRC("Got UDP packet."); + + IPV6_MUTEX_UNLOCK(); + err_code = udp_input(p_interface, p_iphdr, p_pbuffer); + IPV6_MUTEX_LOCK(); + + break; + + default: + IPV6_ERR("Got unsupported protocol packet. Protocol ID = 0x%x!", + p_iphdr->next_header); + +#if (IPV6_ENABLE_USNUPORTED_PROTOCOLS_TO_APPLICATION == 1) + app_notify_rx_data(p_interface, p_pbuffer); +#endif + break; + } + + // Free packet buffer unless marked explicitly as pending + if (err_code != IOT_IPV6_ERR_PENDING) + { + UNUSED_VARIABLE(iot_pbuffer_free(p_pbuffer, true)); + } + + return err_code; +} + + +/**@brief Function for receiving 6LoWPAN module events. + * + * @param[in] p_6lo_interface Pointer to 6LoWPAN interface. + * @param[in] p_6lo_event Pointer to 6LoWPAN related event. + * + * @return None. + */ +static void ble_6lowpan_evt_handler(iot_interface_t * p_interface, + ble_6lowpan_event_t * p_6lo_event) +{ + bool rx_failure = false; + uint32_t err_code; + uint32_t interface_id; + iot_pbuffer_t * p_pbuffer; + iot_pbuffer_alloc_param_t pbuff_param; + + IPV6_MUTEX_LOCK(); + + IPV6_ENTRY(); + IPV6_TRC("In 6LoWPAN Handler:"); + + interface_id = interface_get_by_6lo(p_interface); + + switch (p_6lo_event->event_id) + { + case BLE_6LO_EVT_ERROR: + { + IPV6_ERR("Got error, with result %08lx", p_6lo_event->event_result); + break; + } + case BLE_6LO_EVT_INTERFACE_ADD: + { + IPV6_TRC("New interface established!"); + + // Add interface to internal table. + err_code = interface_add(p_interface, &interface_id); + + if (NRF_SUCCESS == err_code) + { + IPV6_TRC("Added new network interface to internal table."); + + err_code = iot_context_manager_table_alloc(p_interface); + + if (err_code == NRF_SUCCESS) + { + IPV6_TRC("Successfully allocated context table!"); + } + else + { + IPV6_ERR("Failed to allocate context table!"); + } + + // Increase number of up interfaces. + m_interfaces_count++; + + // Notify application. + app_notify_interface_add(p_interface); + } + else + { + IPV6_ERR("Cannot add new interface. Table is full."); + } + + break; + } + case BLE_6LO_EVT_INTERFACE_DELETE: + { + IPV6_TRC("Interface disconnected!"); + + if (interface_id < IPV6_MAX_INTERFACE) + { + IPV6_TRC("Removed network interface."); + + // Notify application. + app_notify_interface_delete(p_interface); + + err_code = iot_context_manager_table_free(p_interface); + + if (err_code == NRF_SUCCESS) + { + IPV6_TRC("Successfully freed context table!"); + } + + // Decrease number of up interfaces. + m_interfaces_count--; + + // Remove interface from internal table. + interface_delete(interface_id); + } + break; + } + case BLE_6LO_EVT_INTERFACE_DATA_RX: + { + IPV6_TRC("Got data with size = %d!", + p_6lo_event->event_param.rx_event_param.packet_len); + IPV6_TRC("Data: "); + IPV6_DUMP(p_6lo_event->event_param.rx_event_param.p_packet, + p_6lo_event->event_param.rx_event_param.packet_len); + + if (interface_id < IPV6_MAX_INTERFACE) + { + if (p_6lo_event->event_result == NRF_ERROR_NOT_FOUND) + { + IPV6_ERR("Cannot restore IPv6 addresses!"); + IPV6_ERR("Source CID = 0x%x, Destination CID = 0x%x", + p_6lo_event->event_param.rx_event_param.rx_contexts.src_cntxt_id, + p_6lo_event->event_param.rx_event_param.rx_contexts.dest_cntxt_id); + + // Indicates failure. + rx_failure = true; + break; + } + + // Check if packet is for us. + ipv6_addr_t * p_addr = + (ipv6_addr_t *)&p_6lo_event->event_param.rx_event_param.p_packet[DEST_ADDR_OFFSET]; + + err_code = addr_check(interface_id, p_addr, true); + + // If no address found - drop message. + if (err_code != NRF_SUCCESS) + { + IPV6_ERR("Packet received on unknown address!"); + rx_failure = true; + break; + } + + // Try to allocate pbuffer, with no memory. + pbuff_param.flags = PBUFFER_FLAG_NO_MEM_ALLOCATION; + pbuff_param.type = RAW_PACKET_TYPE; + pbuff_param.length = p_6lo_event->event_param.rx_event_param.packet_len; + + // Try to allocate pbuffer for receiving data. + err_code = iot_pbuffer_allocate(&pbuff_param, &p_pbuffer); + + if (err_code == NRF_SUCCESS) + { + p_pbuffer->p_memory = p_6lo_event->event_param.rx_event_param.p_packet; + p_pbuffer->p_payload = p_pbuffer->p_memory + IPV6_IP_HEADER_SIZE; + p_pbuffer->length -= IPV6_IP_HEADER_SIZE; + + // Execute multiplexer. + err_code = ipv6_input(p_interface, p_pbuffer); + + if (err_code != NRF_SUCCESS) + { + IPV6_ERR("Failed while processing packet, error = 0x%08lX!", err_code); + } + } + else + { + IPV6_ERR("Failed to allocate packet buffer!"); + rx_failure = true; + } + } + else + { + IPV6_ERR("[6LOWPAN]: Got data to unknown interface!"); + rx_failure = true; + } + + break; + } + default: + break; + } + + if (rx_failure == true) + { + UNUSED_VARIABLE(nrf_free(p_6lo_event->event_param.rx_event_param.p_packet)); + } + + IPV6_EXIT(); + + IPV6_MUTEX_UNLOCK(); +} + + +uint32_t ipv6_init(const ipv6_init_t * p_init) +{ + uint32_t index; + uint32_t err_code; + ble_6lowpan_init_t init_params; + + NULL_PARAM_CHECK(p_init); + NULL_PARAM_CHECK(p_init->p_eui64); + NULL_PARAM_CHECK(p_init->event_handler); + + SDK_MUTEX_INIT(m_ipv6_mutex); + IPV6_MUTEX_LOCK(); + + IPV6_ENTRY(); + + // Initialize related modules. + UNUSED_VARIABLE(nrf_mem_init()); + UNUSED_VARIABLE(iot_pbuffer_init()); + + // Initialize submodules of IPv6 stack. + UNUSED_VARIABLE(udp_init()); + UNUSED_VARIABLE(icmp6_init()); + + // Initialize context manager. + UNUSED_VARIABLE(iot_context_manager_init()); + + IPV6_ADDRESS_INITIALIZE(IPV6_ADDR_ANY); + + // Set application event handler. + m_event_handler = p_init->event_handler; + + // Clear number of interfaces. + m_interfaces_count = 0; + + // Clear network interfaces. + for (index = 0; index < IPV6_MAX_INTERFACE; index++) + { + interface_reset(&m_interfaces[index]); + } + + // Clear all addresses. + for (index = 0; index < IPV6_MAX_ADDRESS_COUNT; index++) + { + addr_free(index, false); + } + + // 6LoWPAN module initialization. + init_params.p_eui64 = p_init->p_eui64; + init_params.event_handler = ble_6lowpan_evt_handler; + + err_code = ble_6lowpan_init(&init_params); + + IPV6_EXIT(); + + IPV6_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t ipv6_address_set(const iot_interface_t * p_interface, + const ipv6_addr_conf_t * p_addr) +{ + VERIFY_MODULE_IS_INITIALIZED(); + + NULL_PARAM_CHECK(p_addr); + NULL_PARAM_CHECK(p_interface); + + uint32_t err_code; + + IPV6_MUTEX_LOCK(); + + IPV6_ENTRY(); + + err_code = addr_set(p_interface, p_addr); + + IPV6_EXIT(); + + IPV6_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t ipv6_address_check(const iot_interface_t * p_interface, + const ipv6_addr_t * p_addr) +{ + VERIFY_MODULE_IS_INITIALIZED(); + + NULL_PARAM_CHECK(p_addr); + NULL_PARAM_CHECK(p_interface); + + uint32_t err_code; + + IPV6_MUTEX_LOCK(); + + IPV6_ENTRY(); + + uint32_t interface_id = (uint32_t)p_interface->p_upper_stack; + + err_code = addr_check(interface_id, p_addr, false); + + IPV6_EXIT(); + + IPV6_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t ipv6_address_find_best_match(iot_interface_t ** pp_interface, + ipv6_addr_t * p_addr_r, + const ipv6_addr_t * p_addr_f) +{ + VERIFY_MODULE_IS_INITIALIZED(); + + NULL_PARAM_CHECK(p_addr_f); + NULL_PARAM_CHECK(pp_interface); + + uint32_t index; + uint32_t err_code; + uint32_t addr_index; + uint32_t match_temp = 0; + uint32_t match_best = 0; + ipv6_addr_t * p_best_addr = NULL; + + IPV6_MUTEX_LOCK(); + + err_code = interface_find(pp_interface, p_addr_f); + + if (err_code == NRF_SUCCESS && p_addr_r) + { + uint32_t interface_id = (uint32_t)(*pp_interface)->p_upper_stack; + + for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++) + { + addr_index = m_interfaces[interface_id].addr_range[index]; + + if (addr_index != IPV6_INVALID_ADDR_INDEX) + { + if (m_address_table[addr_index].state == IPV6_ADDR_STATE_PREFERRED) + { + match_temp = addr_bit_equal(p_addr_f, &m_address_table[addr_index].addr); + + if (match_temp >= match_best) + { + match_best = match_temp; + p_best_addr = &m_address_table[addr_index].addr; + } + } + } + } + + // No address found. + if (p_best_addr == NULL) + { + // Set undefined :: address. + IPV6_ADDRESS_INITIALIZE(p_addr_r); + } + else + { + memcpy(p_addr_r->u8, p_best_addr->u8, IPV6_ADDR_SIZE); + } + } + + IPV6_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t ipv6_address_remove(const iot_interface_t * p_interface, + const ipv6_addr_t * p_addr) +{ + VERIFY_MODULE_IS_INITIALIZED(); + + NULL_PARAM_CHECK(p_addr); + NULL_PARAM_CHECK(p_interface); + + uint32_t index; + uint32_t err_code; + uint32_t addr_index; + + IPV6_MUTEX_LOCK(); + + IPV6_ENTRY(); + + uint32_t interface_id = (uint32_t)p_interface->p_upper_stack; + + err_code = (IOT_IPV6_ERR_BASE | NRF_ERROR_NOT_FOUND); + + for (index = 0; index < IPV6_MAX_ADDRESS_PER_INTERFACE; index++) + { + addr_index = m_interfaces[interface_id].addr_range[index]; + + if (addr_index != IPV6_INVALID_ADDR_INDEX) + { + if (0 == IPV6_ADDRESS_CMP(&m_address_table[addr_index].addr, p_addr)) + { + m_interfaces[interface_id].addr_range[index] = IPV6_INVALID_ADDR_INDEX; + + // Remove address if no reference to interface found. + addr_free(index, true); + + err_code = NRF_SUCCESS; + + break; + } + } + } + + IPV6_EXIT(); + + IPV6_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t ipv6_send(const iot_interface_t * p_interface, iot_pbuffer_t * p_packet) +{ + VERIFY_MODULE_IS_INITIALIZED(); + + NULL_PARAM_CHECK(p_packet); + NULL_PARAM_CHECK(p_interface); + + uint32_t err_code; + + IPV6_MUTEX_LOCK(); + + IPV6_ENTRY(); + + err_code = ble_6lowpan_interface_send(p_interface, + p_packet->p_payload, + p_packet->length); + + if (err_code != NRF_SUCCESS) + { + IPV6_ERR("Cannot send packet!"); + } + + // Free pbuffer, without freeing memory. + UNUSED_VARIABLE(iot_pbuffer_free(p_packet, false)); + + IPV6_EXIT(); + + IPV6_MUTEX_UNLOCK(); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.c new file mode 100644 index 0000000..6611396 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.c @@ -0,0 +1,467 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "nrf_soc.h" +#include "nordic_common.h" +#include "sdk_common.h" +#include "sdk_config.h" +#include "iot_common.h" +#include "iot_pbuffer.h" +#include "mem_manager.h" + +#if IOT_PBUFFER_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME pbuffer + +#define NRF_LOG_LEVEL IOT_PBUFFER_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR IOT_PBUFFER_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR IOT_PBUFFER_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define PBUFFER_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define PBUFFER_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define PBUFFER_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define PBUFFER_ENTRY() PBUFFER_TRC(">> %s", __func__) +#define PBUFFER_EXIT() PBUFFER_TRC("<< %s", __func__) + +#else // IOT_PBUFFER_CONFIG_LOG_ENABLED + +#define PBUFFER_TRC(...) /**< Disables traces. */ +#define PBUFFER_DUMP(...) /**< Disables dumping of octet streams. */ +#define PBUFFER_ERR(...) /**< Disables error logs. */ + +#define PBUFFER_ENTRY(...) +#define PBUFFER_EXIT(...) + +#endif // IOT_PBUFFER_CONFIG_LOG_ENABLED + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * BLE_HPS_DISABLE_API_PARAM_CHECK should be defined to disable these checks. + * + * @{ + */ +#if (IOT_PBUFFER_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Macro to check is module is initialized before requesting one of the module procedures. */ +#define VERIFY_MODULE_IS_INITIALIZED() \ + if (m_initialization_state == false) \ + { \ + return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_PBUFFER_ERR_BASE); \ + } + +/**@brief Macro to check is module is initialized before requesting one of the module + procedures but does not use any return code. */ +#define VERIFY_MODULE_IS_INITIALIZED_VOID() \ + if (m_initialization_state == false) \ + { \ + return; \ + } + +/** + * @brief Verify NULL parameters are not passed to API by application. + */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_PBUFFER_ERR_BASE); \ + } + +/** + * @brief Verify Type field has valid value. + */ +#define VERIFY_PBUFFER_TYPE(TYPE) \ + if (((TYPE) == 0) || ((TYPE) > COAP_PACKET_TYPE)) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_PBUFFER_ERR_BASE); \ + } + + +/** + * @brief Verify flags field has valid value. + */ +#define VERIFY_PBUFFER_FLAGS(FLAG) \ + if ((uint32_t)(FLAG) > PBUFFER_FLAG_NO_MEM_ALLOCATION) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_PBUFFER_ERR_BASE); \ + } + +/** + * @brief Verify flags field has valid value. + */ +#define VERIFY_NON_ZERO_LENGTH(LEN) \ + if ((LEN) ==0) \ + { \ + return (NRF_ERROR_INVALID_LENGTH | IOT_PBUFFER_ERR_BASE); \ + } + +#else //IOT_PBUFFER_DISABLE_API_PARAM_CHECK + +#define VERIFY_MODULE_IS_INITIALIZED() +#define VERIFY_MODULE_IS_INITIALIZED_VOID() +#define NULL_PARAM_CHECK(PARAM) +#define VERIFY_PBUFFER_TYPE(TYPE) +#define VERIFY_PBUFFER_FLAGS(FLAG) +#define VERIFY_NON_ZERO_LENGTH(LEN) + +#endif //IOT_PBUFFER_DISABLE_API_PARAM_CHECK + +/** + * @defgroup ble_ipsp_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case need arises to use an alternative architecture. + * @{ + */ +#define PBUFFER_MUTEX_LOCK() SDK_MUTEX_LOCK(m_pbuffer_mutex) /**< Lock module using mutex */ +#define PBUFFER_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_pbuffer_mutex) /**< Unlock module using mutex */ +/** @} */ + +/** @brief Packet buffer type managed by the module. */ +typedef struct +{ + iot_pbuffer_t buffer; /**< Packet buffer being managed. */ + uint32_t allocated_length; /**< Length allocated for the buffer. */ +}pbuffer_t; + +SDK_MUTEX_DEFINE(m_pbuffer_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ +static bool m_initialization_state = false; /**< Variable to maintain module initialization state. */ +static pbuffer_t m_pbuffer[IOT_PBUFFER_MAX_COUNT]; /**< Table of packet buffers managed by the module. */ + + +/**@brief Initializes packet buffer. */ +static void pbuffer_init(pbuffer_t * p_buffer) +{ + p_buffer->buffer.p_memory = NULL; + p_buffer->buffer.p_payload = NULL; + p_buffer->buffer.length = 0; + p_buffer->buffer.type = UNASSIGNED_TYPE; + p_buffer->allocated_length = 0; +} + + +/**@brief Get size of offset to be used based on the requested type. */ +static uint32_t type_offset_get(iot_pbuffer_type_t type) +{ + uint32_t offset = 0; + + switch (type) + { + case RAW_PACKET_TYPE: + { + offset = 0; + break; + } + case IPV6_PACKET_TYPE: + { + offset = IPV6_IP_HEADER_SIZE; + break; + } + case ICMP6_PACKET_TYPE: + { + offset = IPV6_IP_HEADER_SIZE + ICMP6_HEADER_SIZE; + break; + } + case UDP6_PACKET_TYPE: // Fall through. + { + offset = IPV6_IP_HEADER_SIZE + UDP_HEADER_SIZE; + break; + } + case COAP_PACKET_TYPE: + { + offset = IPV6_IP_HEADER_SIZE + UDP_HEADER_SIZE + COAP_HEADER_SIZE; + break; + } + default: + { + // Should never happen. + break; + } + } + + return offset; +} + + +/**@brief Allocates 'length' sized packet buffer. */ +static uint32_t pbuffer_allocate(pbuffer_t ** pp_buffer, uint32_t length, iot_pbuffer_flags_t flags) +{ + uint32_t index; + uint32_t err_code = (NRF_ERROR_NO_MEM | IOT_PBUFFER_ERR_BASE); + + + for (index = 0; index < IOT_PBUFFER_MAX_COUNT; index ++) + { + if (m_pbuffer[index].allocated_length == 0) + { + // Found a free buffer, allocate. + PBUFFER_TRC("Found free buffer. Requesting memory allocation."); + + m_pbuffer[index].allocated_length = length; + + if (flags == PBUFFER_FLAG_DEFAULT) + { + err_code = nrf_mem_reserve(&m_pbuffer[index].buffer.p_memory, &m_pbuffer[index].allocated_length); + if (err_code == NRF_SUCCESS) + { + PBUFFER_TRC("Allocated pbuffer at index 0x%08lX", index); + (*pp_buffer) = &m_pbuffer[index]; + } + else + { + PBUFFER_ERR("Failed to allocate memory for packet buffer of size %ld", length); + m_pbuffer[index].allocated_length = 0; + } + } + else + { + PBUFFER_TRC("Allocated pbuffer at index 0x%08lX without any memory allocation.", index); + (*pp_buffer) = &m_pbuffer[index]; + err_code = NRF_SUCCESS; + } + break; + } + } + + return err_code; +} + + +/**@brief Finds the internal buffer based on the external iot_pbuffer_t. */ +static uint32_t pbuffer_find(pbuffer_t ** p_internal_buffer, iot_pbuffer_t * p_buffer) +{ + const uint32_t size = sizeof (pbuffer_t); + const uint32_t diff = (((uint32_t)p_buffer) - ((uint32_t)m_pbuffer)); + + if ((diff > (size * IOT_PBUFFER_MAX_COUNT)) || + ((diff % size) != 0)) + { + return (NRF_ERROR_INVALID_ADDR | IOT_PBUFFER_ERR_BASE); + } + + (*p_internal_buffer) = (pbuffer_t *) p_buffer; + + return NRF_SUCCESS; +} + + +uint32_t iot_pbuffer_init(void) +{ + uint32_t index; + + PBUFFER_ENTRY(); + + SDK_MUTEX_INIT(m_pbuffer_mutex); + + PBUFFER_MUTEX_LOCK(); + + for (index = 0; index < IOT_PBUFFER_MAX_COUNT; index++) + { + pbuffer_init(&m_pbuffer[index]); + } + + m_initialization_state = true; + + PBUFFER_EXIT(); + + PBUFFER_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t iot_pbuffer_allocate(iot_pbuffer_alloc_param_t * p_param, + iot_pbuffer_t ** pp_pbuffer) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_param); + NULL_PARAM_CHECK(pp_pbuffer); + VERIFY_PBUFFER_TYPE(p_param->type); + VERIFY_PBUFFER_FLAGS(p_param->flags); + VERIFY_NON_ZERO_LENGTH(p_param->length); + + PBUFFER_ENTRY(); + + PBUFFER_MUTEX_LOCK(); + + uint32_t err_code; + uint32_t offset; + pbuffer_t * p_alloc_buffer; + + // Get offset to be added to length. + offset = type_offset_get(p_param->type); + + err_code = pbuffer_allocate(&p_alloc_buffer, ((p_param->length) + offset), p_param->flags); + if (err_code == NRF_SUCCESS) + { + p_alloc_buffer->buffer.length = p_param->length; + p_alloc_buffer->buffer.type = p_param->type; + + if (p_param->flags != PBUFFER_FLAG_NO_MEM_ALLOCATION) + { + p_alloc_buffer->buffer.p_payload = ((p_alloc_buffer->buffer.p_memory) + offset); + } + + (*pp_pbuffer) = &p_alloc_buffer->buffer; + } + + PBUFFER_MUTEX_UNLOCK(); + + PBUFFER_EXIT(); + + return err_code; +} + + +uint32_t iot_pbuffer_reallocate(iot_pbuffer_alloc_param_t * p_param, + iot_pbuffer_t * p_pbuffer) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_param); + NULL_PARAM_CHECK(p_pbuffer); + VERIFY_PBUFFER_TYPE(p_param->type); + VERIFY_PBUFFER_FLAGS(p_param->flags); + VERIFY_NON_ZERO_LENGTH(p_param->length); + + PBUFFER_ENTRY(); + + PBUFFER_MUTEX_LOCK(); + + uint32_t err_code; + uint32_t realloc_len; + pbuffer_t * p_alloc_buffer; + + // Ensure pointer provided is in the ranged managed by the module. + err_code = pbuffer_find(&p_alloc_buffer, p_pbuffer); + + if (err_code == NRF_SUCCESS) + { + // Get realloc_len to be added to length. + const uint32_t offset = type_offset_get(p_param->type); + realloc_len = p_param->length + offset; + + // Check if requested length cannot be accommodated in the allocated buffer. + if (realloc_len > p_alloc_buffer->allocated_length) + { + // No, it cannot be, request a new buffer. + uint8_t * p_new_mem; + + if (p_param->flags != PBUFFER_FLAG_NO_MEM_ALLOCATION) + { + err_code = nrf_mem_reserve(&p_new_mem, &realloc_len); + if (err_code == NRF_SUCCESS) + { + // Copy data into the new buffer. + memcpy (p_new_mem, + p_pbuffer->p_memory, + p_alloc_buffer->allocated_length); + + // Now free the old buffer. Perform the free + nrf_free(p_alloc_buffer->buffer.p_memory); + + p_alloc_buffer->allocated_length = realloc_len; + p_alloc_buffer->buffer.p_memory = p_new_mem; + p_alloc_buffer->buffer.length = p_param->length; + } + } + } + + if (err_code == NRF_SUCCESS) + { + p_alloc_buffer->buffer.length = p_param->length; + p_alloc_buffer->buffer.type = p_param->type; + + if (p_param->flags == PBUFFER_FLAG_DEFAULT) + { + p_alloc_buffer->buffer.p_payload = (p_alloc_buffer->buffer.p_memory + offset); + } + } + } + else + { + PBUFFER_ERR("Cannot find buffer to be freed."); + } + + PBUFFER_MUTEX_UNLOCK(); + + PBUFFER_EXIT(); + + return err_code; +} + + +uint32_t iot_pbuffer_free(iot_pbuffer_t * p_pbuffer, bool free_flag) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_pbuffer); + + PBUFFER_ENTRY(); + + PBUFFER_MUTEX_LOCK(); + + uint32_t err_code; + pbuffer_t * p_alloc_buffer; + + // Ensure pointer provided is in the ranged managed by the module. + err_code = pbuffer_find(&p_alloc_buffer, p_pbuffer); + if (err_code == NRF_SUCCESS) + { + if (free_flag == true) + { + nrf_free(p_alloc_buffer->buffer.p_memory); + } + pbuffer_init(p_alloc_buffer); + } + else + { + PBUFFER_ERR("Cannot find buffer to be freed."); + } + + PBUFFER_MUTEX_UNLOCK(); + + PBUFFER_EXIT(); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.h new file mode 100644 index 0000000..e13da3a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/pbuffer/iot_pbuffer.h @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup iot_pbuffer Packet Buffer + * @{ + * @ingroup iot_sdk_stack + * @brief Packet buffer management for IPv6 stack layers to minimize data copy across stack layers. + * + * @details This module interfaces with the Memory Manager to allocate packet buffers + * for the IPv6 stack layers, without each layer having to ensure + * sufficient header space for layers below. + */ +#ifndef IOT_PBUFFER__ +#define IOT_PBUFFER__ + +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief IPv6 packet type identifiers that are needed to ensure that enough + * space is reserved for headers from layers below during memory allocation. + */ +typedef enum +{ + UNASSIGNED_TYPE = 0, /**< Indicates that the packet buffer is unassigned and not in use. */ + RAW_PACKET_TYPE = 1, /**< Raw packet, with no room made for headers of any lower layer. */ + IPV6_PACKET_TYPE = 2, /**< Indicates that the packet buffer is requested for an entire IPv6 packet; pbuffer provisions 40 bytes of IPv6 header. */ + ICMP6_PACKET_TYPE = 3, /**< Indicates that the packet buffer is requested for an ICMPv6 packet, and provision for 40 bytes of IPv6 header is made by pbuffer. */ + UDP6_PACKET_TYPE = 4, /**< Indicates that the packet buffer is requested for a UDP packet, and provision for 40 bytes of IPv6 header and UDP header is made by pbuffer. */ + COAP_PACKET_TYPE = 5 /**< Indicates that the packet buffer is requested for a CoAP packet, and provision for 4 bytes of CoAP header, 8 bytes of UDP header, and 40 bytes of IPv6 header is made. */ +}iot_pbuffer_type_t; + +/**@brief Additional information that must be provided to the module during allocation + * or reallocation to ensure optimal utilization of memory and avoid unnecessary data + * copies. + */ +typedef enum +{ + PBUFFER_FLAG_DEFAULT = 0, /**< Default behavior with respect to memory allocation when allocating packet buffer. Memory will be allocated for the length indicated by this default.*/ + PBUFFER_FLAG_NO_MEM_ALLOCATION = 1, /**< Only allocate packet buffer, not memory. This is needed when a packet already exists and the packet buffer is needed only to feed it to the IPv6 stack.*/ +}iot_pbuffer_flags_t; + +/**@brief Packet buffer used for exchanging IPv6 payload across layers in both receive and transmit + * paths. + */ +typedef struct +{ + iot_pbuffer_type_t type; /**< Determines if any offset for lower layers must be provisioned for in the stack. */ + uint8_t * p_memory; /**< Pointer to actual memory allocated for the buffer. */ + uint8_t * p_payload; /**< Pointer to memory where the payload for the layer that allocates the packet buffer should be contained. */ + uint32_t length; /**< Length of the payload of the layer processing it. This value can be modified by each layer of the IPv6 stack based on the required header information added by each.*/ +}iot_pbuffer_t; + + +/**@brief Parameters required to allocate the packet buffer. */ +typedef struct +{ + iot_pbuffer_type_t type; /**< Payload type for which the packet buffer is requested to be allocated or reallocated. */ + iot_pbuffer_flags_t flags; /**< Flags that indicate if memory allocation is needed or not. */ + uint32_t length; /**< Length of payload for which the packet buffer is requested. */ +}iot_pbuffer_alloc_param_t; + + +/**@brief Function for initializing the module. + * + * @retval NRF_SUCCESS If the module was successfully initialized. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t iot_pbuffer_init(void); + + +/**@brief Function for allocating a packet buffer. + * + * @param[in] p_param Pointer to allocation parameters that indicate the length of the payload requested, + * the type of payload, and additional information using the flags. This + * parameter cannot be NULL. + * @param[out] pp_pbuffer Pointer to allocated packet buffer. This parameter shall + * not be NULL. + * + * @retval NRF_SUCCESS If the packet buffer was successfully allocated. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t iot_pbuffer_allocate(iot_pbuffer_alloc_param_t * p_param, + iot_pbuffer_t ** pp_pbuffer); + + +/**@brief Function for reallocating a packet buffer. + * + * Reallocation requests are treated as follows: + * - If the requested reallocation is less than or equal to the allocated size, + * no data is moved, and the function returns NRF_SUCCESS. + * - If the requested reallocation is more than what is allocated, the function + * requests new memory, backs up existing data, and then frees the previously + * allocated memory. + * - If reallocation is requested with the PBUFFER_FLAG_NO_MEM_ALLOCATION flag, + * the function does not free previously allocated memory or copy it to the + * new location. In this case, the application that uses the pbuffer must + * decide when to move previously allocated memory and when to free it and + * handle this. + * + * @param[in] p_param Pointer to reallocation parameters that indicate the length of the payload requested, + * the type of payload, and additional information using the flags. This + * parameter cannot be NULL. + * @param[in] p_pbuffer Pointer to the packet buffer being reallocated. This parameter shall + * not be NULL. + * + * @retval NRF_SUCCESS If the packet buffer was successfully reallocated. Otherwise, an error code that indicates the reason for the failure is returned. + */ +uint32_t iot_pbuffer_reallocate(iot_pbuffer_alloc_param_t * p_param, + iot_pbuffer_t * p_pbuffer); + + +/**@brief Function for freeing a packet buffer. + * + * This function frees the packet buffer. If the parameter free_flag is set, the + * function tries to free the memory allocated as well. This action is performed + * irrespective of whether the memory was allocated using the PBUFFER_FLAG_DEFAULT or + * the PBUFFER_FLAG_NO_MEM_ALLOCATION flag. + * + * @param[in] p_pbuffer Pointer to the packet buffer requested to be freed. This parameter shall + * not be NULL. + * @param[in] free_flag Indicates if the allocated memory should be freed or not when freeing the + * packet buffer. + * + * @retval NRF_SUCCESS If the packet buffer was successfully freed. Otherwise, an error code that indicates the reason for the failure is returned. + * + */ +uint32_t iot_pbuffer_free(iot_pbuffer_t * p_pbuffer, bool free_flag); + +#ifdef __cplusplus +} +#endif + +#endif // IOT_PBUFFER__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.c new file mode 100644 index 0000000..2b79462 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.c @@ -0,0 +1,607 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "udp_api.h" +#include "ipv6_api.h" +#include "app_error.h" +#include "sdk_common.h" +#include "sntp_client.h" + +#if SNTP_CLIENT_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME sntp + +#define NRF_LOG_LEVEL SNTP_CLIENT_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR SNTP_CLIENT_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR SNTP_CLIENT_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define SNTP_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define SNTP_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define SNTP_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define SNTP_ENTRY() SNTP_TRC(">> %s", __func__) +#define SNTP_EXIT() SNTP_TRC("<< %s", __func__) + +#else // SNTP_CLIENT_CONFIG_LOG_ENABLED + +#define SNTP_TRC(...) /**< Disables traces. */ +#define SNTP_DUMP(...) /**< Disables dumping of octet streams. */ +#define SNTP_ERR(...) /**< Disables error logs. */ + +#define SNTP_ENTRY(...) +#define SNTP_EXIT(...) + +#endif // SNTP_CLIENT_CONFIG_LOG_ENABLED + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * SNTP_CLIENT_DISABLE_API_PARAM_CHECK should be defined to disable these checks. + * + * @{ + */ +#if (SNTP_CLIENT_DISABLE_API_PARAM_CHECK == 0) + +/** + * @brief Verify NULL parameters are not passed to an API by application. + */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_NTP_ERR_BASE); \ + } + +/** + * @brief Verify that not zero is passed to an API by application. + */ +#define ZERO_PARAM_CHECK(PARAM) \ + if ((PARAM) == 0x00) \ + { \ + return (NRF_ERROR_NULL | IOT_NTP_ERR_BASE); \ + } + +/** + * @brief Macro to check if module is initialized. + */ +#define VERIFY_MODULE_IS_INITIALIZED() \ + if (m_sntp_client_state == SNTP_CLIENT_STATE_UNINITIALIZED) \ + { \ + return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_NTP_ERR_BASE); \ + } + +#else // SNTP_CLIENT_DISABLE_API_PARAM_CHECK + +#define NULL_PARAM_CHECK(PARAM) +#define ZERO_PARAM_CHECK(PARAM) +#define VERIFY_MODULE_IS_INITIALIZED() + +#endif //SNTP_CLIENT_DISABLE_API_PARAM_CHECK +/** @} */ + +/** + * @defgroup ble_sntp_c_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case need arises to use an alternative architecture. + * @{ + */ +#define SNTP_C_MUTEX_LOCK() SDK_MUTEX_LOCK(m_sntp_c_mutex) /**< Lock module using mutex */ +#define SNTP_C_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_sntp_c_mutex) /**< Unlock module using mutex */ +/** @} */ + +#define TIME_AT_1970 2208988800UL // Number of seconds between 1st Jan 1900 and 1st Jan 1970, for NTP<->Unix time conversion. +#define PROTOCOL_MODE_SERVER 4 + +/**@brief NTP Header Format. */ +typedef struct +{ + uint8_t flags; /**< Please see RFC 4330. */ + uint8_t stratum; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */ + uint8_t poll_interval; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */ + uint8_t precision; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */ + uint32_t root_delay; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */ + uint32_t root_dispersion; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */ + uint32_t reference_id; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */ + uint32_t reference_timestamp[2]; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */ + uint32_t originate_timestamp[2]; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */ + uint32_t receive_timestamp[2]; /**< Please see RFC 4330. This field is significant only in SNTP server messages. */ + uint32_t transmit_timestamp[2]; /**< Please see RFC 4330. */ +} ntp_header_t; + +typedef enum +{ + SNTP_CLIENT_STATE_UNINITIALIZED = 1, + SNTP_CLIENT_STATE_IDLE, + SNTP_CLIENT_STATE_BUSY +} sntp_client_state_t; + +typedef struct +{ + time_t unix_time; + iot_timer_time_in_ms_t wall_clock_value; +} local_timestamp_t; + +SDK_MUTEX_DEFINE(m_sntp_c_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ +static sntp_client_state_t m_sntp_client_state = SNTP_CLIENT_STATE_UNINITIALIZED; +static ipv6_addr_t * m_p_ntp_server_address; +static uint16_t m_ntp_server_port; +static bool m_do_sync_local_time; +static iot_timer_time_in_ms_t m_time_of_last_transmission; +static uint8_t m_retransmission_count; +static sntp_evt_handler_t m_app_evt_handler; +static udp6_socket_t m_udp_socket; +static local_timestamp_t m_local_time; + +/**@brief Function for checking if a received NTP packet is valid. + * + * @param[in] p_ntp_response Pointer to the NTP packet header. + * + */ +static bool is_response_valid(ntp_header_t * p_ntp_response) +{ + if (((p_ntp_response->transmit_timestamp[0] == 0x00) && \ + (p_ntp_response->transmit_timestamp[1] == 0x00)) || \ + ((p_ntp_response->flags & 0x38) == 0x00) || \ + ((p_ntp_response->flags & 0x07) != PROTOCOL_MODE_SERVER)) + { + return false; + } + + return true; +} + + +/**@brief Callback handler to receive data on the UDP port. + * + * @param[in] p_socket Socket identifier. + * @param[in] p_ip_header IPv6 header containing source and destination addresses. + * @param[in] p_udp_header UDP header identifying local and remote endpoints. + * @param[in] process_result Result of data reception, there could be possible errors like + * invalid checksum etc. + * @param[in] p_rx_packet Packet buffer containing the received data packet. + * + * @retval NRF_SUCCESS Indicates received data was handled successfully, else an an + * error code indicating reason for failure.. + */ +static uint32_t port_data_callback(const udp6_socket_t * p_socket, + const ipv6_header_t * p_ip_header, + const udp6_header_t * p_udp_header, + uint32_t process_result, + iot_pbuffer_t * p_rx_packet) +{ + SNTP_C_MUTEX_LOCK(); + + SNTP_ENTRY(); + + uint32_t err_code = NRF_SUCCESS; + ntp_header_t * p_ntp_header = (ntp_header_t *)p_rx_packet->p_payload; + + if (m_sntp_client_state != SNTP_CLIENT_STATE_BUSY) + { + SNTP_ERR("Unexpected NTP response received."); + + SNTP_C_MUTEX_UNLOCK(); + + SNTP_EXIT(); + return (NRF_ERROR_INVALID_STATE | IOT_NTP_ERR_BASE); + } + else + { + // Check UDP process result and data length. + if ((process_result != NRF_SUCCESS) || p_rx_packet->length < sizeof(ntp_header_t)) + { + SNTP_ERR("Received erroneous NTP response."); + + m_sntp_client_state = SNTP_CLIENT_STATE_IDLE; + m_retransmission_count = 0; + m_do_sync_local_time = false; + err_code = (NRF_ERROR_INVALID_DATA | IOT_NTP_ERR_BASE); + + SNTP_C_MUTEX_UNLOCK(); + + if (m_app_evt_handler != NULL) + { + m_app_evt_handler(&(p_ip_header->srcaddr), p_udp_header->srcport, err_code, \ + (sntp_client_cb_param_t){ .callback_data = 0x00 }); + } + + SNTP_EXIT(); + return err_code; + } + else + { + if (!is_response_valid(p_ntp_header)) + { + SNTP_ERR("Received bad NTP response."); + + m_sntp_client_state = SNTP_CLIENT_STATE_IDLE; + m_retransmission_count = 0; + m_do_sync_local_time = false; + err_code = NTP_SERVER_BAD_RESPONSE; + + SNTP_C_MUTEX_UNLOCK(); + + if (m_app_evt_handler != NULL) + { + m_app_evt_handler(&(p_ip_header->srcaddr), \ + p_udp_header->srcport, \ + err_code, \ + (sntp_client_cb_param_t){ .callback_data = 0x00 }); + } + + SNTP_EXIT(); + return err_code; + } + else + { + // Check if Kiss-o'-Death packet. + if (p_ntp_header->stratum == 0x00) + { + SNTP_TRC("Received Kiss-o'-Death packet."); + + m_sntp_client_state = SNTP_CLIENT_STATE_IDLE; + m_retransmission_count = 0; + m_do_sync_local_time = false; + + SNTP_C_MUTEX_UNLOCK(); + + if (m_app_evt_handler != NULL) + { + m_app_evt_handler(&(p_ip_header->srcaddr), p_udp_header->srcport, \ + NTP_SERVER_KOD_PACKET_RECEIVED, \ + (sntp_client_cb_param_t){ .callback_data \ + = p_ntp_header->reference_id }); + } + + SNTP_EXIT(); + return NRF_SUCCESS; + } + else + { + // Process decent NTP response. + time_t time_from_response = (HTONL(p_ntp_header->transmit_timestamp[0])) - \ + TIME_AT_1970; + + if (m_do_sync_local_time) + { + iot_timer_time_in_ms_t wall_clock_value; + UNUSED_VARIABLE(iot_timer_wall_clock_get(&wall_clock_value)); + m_local_time.unix_time = time_from_response; + m_local_time.wall_clock_value = wall_clock_value; + m_do_sync_local_time = false; + } + + m_sntp_client_state = SNTP_CLIENT_STATE_IDLE; + m_retransmission_count = 0; + + SNTP_C_MUTEX_UNLOCK(); + + if (m_app_evt_handler != NULL) + { + m_app_evt_handler(&(p_ip_header->srcaddr), \ + p_udp_header->srcport, \ + NRF_SUCCESS, \ + (sntp_client_cb_param_t){ .time_from_server = \ + time_from_response }); + } + + SNTP_EXIT(); + return NRF_SUCCESS; + } + } + } + } +} + + +uint32_t sntp_client_init(const sntp_client_init_param_t * p_sntp_client_init_param) +{ + NULL_PARAM_CHECK(p_sntp_client_init_param); + ZERO_PARAM_CHECK(p_sntp_client_init_param->local_udp_port); + + SNTP_ENTRY(); + + SDK_MUTEX_INIT(m_sntp_c_mutex); + SNTP_C_MUTEX_LOCK(); + + uint32_t err_code; + + memset(&m_local_time, 0x00, sizeof(m_local_time)); + m_app_evt_handler = p_sntp_client_init_param->app_evt_handler; + + //Request new socket creation. + err_code = udp6_socket_allocate(&m_udp_socket); + + if (err_code == NRF_SUCCESS) + { + // Bind the socket to the local port. + err_code = udp6_socket_bind(&m_udp_socket, \ + IPV6_ADDR_ANY, \ + p_sntp_client_init_param->local_udp_port); + if (err_code == NRF_SUCCESS) + { + //Register data receive callback. + err_code = udp6_socket_recv(&m_udp_socket, port_data_callback); + } + if (err_code != NRF_SUCCESS) + { + //Not all procedures succeeded with allocated socket, hence free it. + UNUSED_VARIABLE(udp6_socket_free(&m_udp_socket)); + } + } + + if (err_code == NRF_SUCCESS) + { + m_sntp_client_state = SNTP_CLIENT_STATE_IDLE; + } + + SNTP_EXIT(); + + SNTP_C_MUTEX_UNLOCK(); + + return err_code; +} + + +static uint32_t local_time_get(time_t * p_local_time) +{ + uint32_t err_code = NRF_SUCCESS; + iot_timer_time_in_ms_t delta_ms; + err_code = iot_timer_wall_clock_delta_get(&m_local_time.wall_clock_value, &delta_ms); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + *p_local_time = m_local_time.unix_time + (delta_ms / 1000); + + return err_code; +} + + +uint32_t sntp_client_local_time_get(time_t * p_current_time) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_current_time); + + uint32_t err_code = NRF_SUCCESS; + + SNTP_ENTRY(); + + SNTP_C_MUTEX_LOCK(); + + err_code = local_time_get(p_current_time); + + SNTP_EXIT(); + + SNTP_C_MUTEX_UNLOCK(); + + return err_code; +} + + +/**@brief Function for sending SNTP query. + * + * @retval NRF_SUCCESS on successful execution of procedure, otherwise an error code indicating reason + * for failure. + */ +static uint32_t sntp_query_send() +{ + uint32_t err_code; + iot_pbuffer_t * p_buffer; + iot_pbuffer_alloc_param_t buffer_param; + time_t current_local_time; + + err_code = local_time_get(¤t_local_time); + if (err_code != NRF_SUCCESS) + { + SNTP_ERR("An error occurred while getting local time value!"); + return err_code; + } + + buffer_param.type = UDP6_PACKET_TYPE; + buffer_param.flags = PBUFFER_FLAG_DEFAULT; + buffer_param.length = sizeof(ntp_header_t); + + UNUSED_VARIABLE(iot_timer_wall_clock_get(&m_time_of_last_transmission)); + + // Allocate packet buffer. + err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer); + + if (err_code == NRF_SUCCESS) + { + ntp_header_t * p_ntp_header = (ntp_header_t *)p_buffer->p_payload; + memset(p_ntp_header, 0x00, sizeof(ntp_header_t)); + + // Fill NTP header fields. + p_ntp_header->flags = 0x1B; // LI = 0; VN = 3; Mode = 3 + p_ntp_header->transmit_timestamp[0] = HTONL((uint32_t)(current_local_time + TIME_AT_1970)); + + // Send NTP query using UDP socket. + err_code = udp6_socket_sendto(&m_udp_socket, \ + m_p_ntp_server_address, \ + m_ntp_server_port, \ + p_buffer); + if (err_code != NRF_SUCCESS) + { + SNTP_ERR("Unable to send query on UDP socket. Reason %08lx.", err_code); + + // Free the allocated buffer as send procedure has failed. + UNUSED_VARIABLE(iot_pbuffer_free(p_buffer, true)); + } + } + else + { + SNTP_ERR("No memory to allocate packet buffer."); + } + + return err_code; +} + + +uint32_t sntp_client_server_query(ipv6_addr_t * p_ntp_server_address, \ + uint16_t ntp_server_udp_port, \ + bool sync_local_time) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_ntp_server_address); + ZERO_PARAM_CHECK(ntp_server_udp_port); + + uint32_t err_code = NRF_SUCCESS; + SNTP_ENTRY(); + + SNTP_C_MUTEX_LOCK(); + + if (m_sntp_client_state != SNTP_CLIENT_STATE_IDLE) + { + SNTP_EXIT(); + return (NRF_ERROR_BUSY | IOT_NTP_ERR_BASE); + } + + m_p_ntp_server_address = p_ntp_server_address; + m_ntp_server_port = ntp_server_udp_port; + m_do_sync_local_time = sync_local_time; + + err_code = sntp_query_send(); + if (err_code == NRF_SUCCESS) + { + m_sntp_client_state = SNTP_CLIENT_STATE_BUSY; + } + + SNTP_EXIT(); + + SNTP_C_MUTEX_UNLOCK(); + + return err_code; +} + + +/**@brief Function for determining whether it is time to retransmit a query. + * + */ +static bool is_it_time_to_retransmit() +{ + uint32_t err_code = NRF_SUCCESS; + iot_timer_time_in_ms_t delta_ms = 0; + + err_code = iot_timer_wall_clock_delta_get(&m_time_of_last_transmission, &delta_ms); + if (err_code != NRF_SUCCESS) + { + return true; + } + if (delta_ms >= (SNTP_RETRANSMISSION_INTERVAL * 1000)) + { + return true; + } + else + { + return false; + } +} + + +void sntp_client_timeout_process(iot_timer_time_in_ms_t wall_clock_value) +{ + SNTP_C_MUTEX_LOCK(); + + UNUSED_PARAMETER(wall_clock_value); + + if (m_sntp_client_state == SNTP_CLIENT_STATE_BUSY) + { + if (is_it_time_to_retransmit()) + { + m_retransmission_count++; + if (m_retransmission_count > SNTP_MAX_RETRANSMISSION_COUNT) + { + m_sntp_client_state = SNTP_CLIENT_STATE_IDLE; + m_retransmission_count = 0; + m_do_sync_local_time = false; + + SNTP_C_MUTEX_UNLOCK(); + + if (m_app_evt_handler != NULL) + { + m_app_evt_handler(m_p_ntp_server_address, \ + m_ntp_server_port, \ + NTP_SERVER_UNREACHABLE, \ + (sntp_client_cb_param_t){ .callback_data = 0x00 }); + } + + SNTP_TRC("NTP server did not respond to query."); + return; + } + else + { + SNTP_TRC("Query retransmission [%d].", m_retransmission_count); + UNUSED_VARIABLE(sntp_query_send()); + } + } + } + + SNTP_C_MUTEX_UNLOCK(); + return; +} + + +uint32_t sntp_client_uninitialize() +{ + VERIFY_MODULE_IS_INITIALIZED(); + + SNTP_ENTRY(); + + SNTP_C_MUTEX_LOCK(); + + // Free UDP socket. + UNUSED_VARIABLE(udp6_socket_free(&m_udp_socket)); + + m_sntp_client_state = SNTP_CLIENT_STATE_UNINITIALIZED; + m_retransmission_count = 0; + m_do_sync_local_time = false; + + SNTP_EXIT(); + + SNTP_C_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.h new file mode 100644 index 0000000..d9cb7eb --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/sntp_client/sntp_client.h @@ -0,0 +1,202 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup sntp_client SNTP Client + * @{ + * @ingroup iot_sdk_stack + * @brief Simple Network Time Protocol (SNTP) client for obtaining and storing local unix time. + * + * @details Concurrent queries are not supported. Exponential-backoff algorithm for + * retransmissions is not implemented, retransmissions are triggered at regular intervals. + * + */ + +#ifndef SNTP_CLIENT_H__ +#define SNTP_CLIENT_H__ + +#include <stdint.h> +/*lint -save -e43 -e1504 */ +#include <time.h> +/*lint -restore */ +#include "sdk_config.h" +#include "nrf_error.h" +#include "ipv6_api.h" +#include "iot_timer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define KISS_CODE_LEN 4 /**< Kiss-o'-Death packets convey kiss codes as 4 character long @c ASCII messages. */ + +/**@brief SNTP client callback parameter. + */ +typedef union +{ + time_t time_from_server; /**< Unix time if a proper proper response is received from an NTP server. */ + uint32_t callback_data; /**< Data pertaining to the event triggering the callback. The kiss code from any Kiss-o'-Death packets. */ +} sntp_client_cb_param_t; + +/**@brief SNTP client callback type. + * + * @details Execution of the callback function marks the completion of an SNTP query. + * The callback will be executed if a response is received from the NTP server, + * or if the server remains unresponsive even after @ref SNTP_MAX_RETRANSMISSION_COUNT + * is reached. + * + * @param[in] p_ntp_srv_addr Pointer to the source IPv6 address of the NTP response, or to + * the IPv6 address of the NTP server targeted by the unsuccessful + * query. + * @param[in] ntp_srv_udp_port The source UDP port of the NTP response, or the UDP port of + * the NTP server targeted by the unsuccessful query. + * @param[in] process_result The value of this parameter reveals whether a response from the + * NTP server or a timeout triggered the callback. + * @param[in] callback_parameter This parameter holds the unix time from the server after a + * successful query, or the kiss code if a Kiss-o'-Death packet + * was received. Otherwise NULL. + * + * @retval None. + * + */ +typedef void (*sntp_evt_handler_t)(const ipv6_addr_t * p_ntp_srv_addr, \ + uint16_t ntp_srv_udp_port, \ + uint32_t process_result, \ + sntp_client_cb_param_t callback_parameter); + +/**@brief SNTP client initialization structure. + * + * @note @ref app_evt_handler can be set to zero to disable callbacks. + */ +typedef struct +{ + sntp_evt_handler_t app_evt_handler; /**< Pointer to the event handler callback function. Triggered by a response from an NTP server to an SNTP query, or a retransmission timeout after @ref SNTP_MAX_RETRANSMISSION_COUNT is reached. */ + uint16_t local_udp_port; /**< Local port used by the UDP socket allocated for the SNTP client module. Cannot be NULL. */ +} sntp_client_init_param_t; + +/** + * @brief Function for initializing the SNTP client module. + * + * @details The SNTP client uses UDP as transport layer, therefore, one UDP socket is allocated + * for the module and is used to transmit any future queries. + * + * @param[in] p_sntp_client_init_param Pointer to the initialization structure for the SNTP client. + * Should not be NULL. + * + * @note The event handler in the initialization structure can be set to NULL to disable callbacks + * from the module. + * + * @retval NRF_SUCCESS Module successfully initialized. + * @retval NRF_ERROR_NULL If @b p_sntp_client_init_param is NULL, or if it points to a local UDP + * port that is NULL. + * + */ +uint32_t sntp_client_init(const sntp_client_init_param_t * p_sntp_client_init_param); + +/** + * @brief Function for uninitializing the SNTP client module. + * + * @details This procedure frees up the UDP socket previously allocated to the module. + * Any pending retransmissions are cleared and no more callbacks will be executed. + * + * @retval NRF_SUCCESS Module successfully uninitialized. + * @retval SDK_ERR_MODULE_NOT_INITIALIZED The module was not initialized. + * + */ +uint32_t sntp_client_uninitialize(void); + +/**@brief Function for sending an SNTP query to the specified NTP server. + * + * @details The local unix time is set to zero (1-Jan-70) when the module is initialized. It can + * be updated by using the @ref sntp_client_server_query procedure. The accuracy of the + * output is depending on the wall clock of the IoT Timer module. + * + * @param[in] p_ntp_server_address Pointer to the IPv6 address of the NTP server. This memory must + * be resident until the query is completed. + * @param[in] ntp_server_udp_port Destination port of the NTP server. The UDP port number + * assigned by the IANA to NTP is 123. + * @param[in] sync_local_time A boolean value telling the module whether to synchronize its + * local clock with any response received from the NTP server. + * + * @retval NRF_SUCCESS SNTP query successfully sent. + * @retval SDK_ERR_MODULE_NOT_INITIALIZED The module was not initialized. + * @retval NRF_ERROR_NULL If @b p_ntp_server_address or @b ntp_server_udp_port + * is a NULL pointer. + * + */ +uint32_t sntp_client_server_query(ipv6_addr_t * p_ntp_server_address, \ + uint16_t ntp_server_udp_port, \ + bool sync_local_time); + +/**@brief Function for getting the local unix time from the module. + * + * @details The local unix time is set to zero (1-Jan-70) when the module is initialized. It can + * be updated by using @ref sntp_client_server_query procedure. The accuracy of the + * output is depending on the wall clock of the IoT Timer module. + * + * @param[out] p_current_time Local unix time. + * + * @retval NRF_SUCCESS Getting locally stored unix time successful. + * @retval SDK_ERR_MODULE_NOT_INITIALIZED The module was not initialized. + * @retval NRF_ERROR_NULL If @b p_current_time is a NULL pointer. + * + */ +uint32_t sntp_client_local_time_get(time_t * p_current_time); + +/**@brief Function for performing retransmissions of SNTP queries. + * + * @details The SNTP client module implements the retransmission mechanism by invoking this + * function periodically. This procedure is to be added to the IoT Timer client list + * and has to be called repeatedly with a minimum period of SNTP_RETRANSMISSION_INTERVAL. + * + * @param[in] wall_clock_value The value of the wall clock that triggered the callback. + * + * @retval None. + * + */ +void sntp_client_timeout_process(iot_timer_time_in_ms_t wall_clock_value); + +#ifdef __cplusplus +} +#endif + +#endif // SNTP_CLIENT_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.c new file mode 100644 index 0000000..65f0ec1 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.c @@ -0,0 +1,2455 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_config.h" +#include "iot_tftp.h" +#include "iot_common.h" +#include "udp_api.h" +#include "app_util.h" + +#if TFTP_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME tftp + +#define NRF_LOG_LEVEL TFTP_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR TFTP_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR TFTP_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define TFTP_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define TFTP_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define TFTP_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define TFTP_ENTRY() TFTP_TRC(">> %s", __func__) +#define TFTP_EXIT() TFTP_TRC("<< %s", __func__) + +#else // TFTP_CONFIG_LOG_ENABLED + +#define TFTP_TRC(...) /**< Disables traces. */ +#define TFTP_DUMP(...) /**< Disables dumping of octet streams. */ +#define TFTP_ERR(...) /**< Disables error logs. */ + +#define TFTP_ENTRY(...) +#define TFTP_EXIT(...) + +#endif // TFTP_CONFIG_LOG_ENABLED +/** + * @defgroup tftp_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case need arises to use an alternative architecture. + * @{ + */ +#define TFTP_MUTEX_LOCK() SDK_MUTEX_LOCK(m_tftp_mutex) /**< Lock module using mutex. */ +#define TFTP_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_tftp_mutex) /**< Unlock module using mutex. */ +/** @} */ + +#define TFTP_HEADER_SIZE 2 /**< uint16_t opcode number. */ +#define TFTP_BLOCK_ID_SIZE 2 /**< uint16_t block id number. */ +#define TFTP_ERR_CODE_SIZE 2 /**< uint16_t error code. */ +#define TFTP_DEFAULT_BLOCK_SIZE 512 /**< uint16_t default data block size. */ +#define TFTP_DEFAULT_PORT 69 /**< uint16_t default TFTP server port number. */ + +/**@brief Supported TFTP options. */ +#define OPTION_MODE_ASCII "netascii" /**< NETASCII mode string defined inside RFC1350. */ +#define OPTION_MODE_OCTET "octet" /**< OCTET mode string defined inside RFC1350. */ +#define OPTION_BLKSIZE "blksize" /**< Block Size option string defined inside RFC2348. */ +#define OPTION_TIMEOUT "timeout" /**< Timeout option string defined inside RFC2349. */ +#define OPTION_SIZE "tsize" /**< Transfer Size option string defined inside RFC2348. */ + +#define NEXT_RETR_MAX_LENGTH 4 /**< Maximum length of TFTP "timeout" option value. */ +#define BLKSIZE_MAX_LENGTH 10 /**< Maximum length of TFTP "blksize" option value. */ +#define FILE_SIZE_MAX_LENGTH 10 /**< Maximum length of TFTP "tsize" option value. */ + +#define OPTION_ERROR_MESSAGE "Unsupported option(s) requested" +#define UDP_ERROR_MSG "UDP Error!" +#define LENGTH_ERROR_MSG "Invalid packet length!" +#define UNINT_ERROR_MSG "Connection reset by peer" +#define ACCESS_ERROR_MSG "Access denied (cannot read/write from file)" +#define OPTION_SIZE_REQUEST_VALUE "0" + +/**@brief TFTP Error codes. */ +#define ERR_UNDEFINED 0 /**< Not defined, see error message (if any). */ +#define ERR_FILE_NOT_FOUND 1 /**< File not found. */ +#define ERR_ACCESS_ERROR 2 /**< Access violation. */ +#define ERR_STORAGE_FULL 3 /**< Disk full or allocation exceeded. */ +#define ERR_INVALID_OP 4 /**< Illegal TFTP operation. */ +#define ERR_INVALID_TID 5 /**< Unknown transfer ID. */ +#define ERR_FILE_EXISTS 6 /**< File already exists. */ +#define ERR_BAD_USER 7 /**< No such user. */ +#define ERR_OPTION_REJECT 8 /**< Reject proposed options. */ + +/**@brief TFTP opcode's. This field specifies type of packet. */ +#define TYPE_RRQ 1 /**< Read request (RRQ). */ +#define TYPE_WRQ 2 /**< Write request (WRQ). */ +#define TYPE_DATA 3 /**< Data (DATA). */ +#define TYPE_ACK 4 /**< Acknowledgment (ACK). */ +#define TYPE_ERR 5 /**< Error (ERROR). */ +#define TYPE_OACK 6 /**< Option Acknowledgment (RRQ/WRQ ACK). */ + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * DNS6_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks. + * + * @{ + */ + +#if (TFTP_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_TFTP_ERR_BASE); \ + } + +#else // TFTP_DISABLE_API_PARAM_CHECK + +#define NULL_PARAM_CHECK(PARAM) + +#endif // DNS6_DISABLE_API_PARAM_CHECK + +/**@brief Check err_code, free p_buffer and return on error. */ +#define PBUFFER_FREE_IF_ERROR(err_code) \ + if (err_code != NRF_SUCCESS) \ + { \ + (void)iot_pbuffer_free(p_buffer, true); \ + return err_code; \ + } + +/**@brief Convert TFTP error code into IOT error with appropriate base. */ +#define CONVERT_TO_IOT_ERROR(error_code) \ + ((IOT_TFTP_ERR_BASE+0x0040) + NTOHS(error_code)) + +/**@brief Convert IOT error into TFTP error code by removing TFTP error base. */ +#define CONVERT_TO_TFTP_ERROR(error_code) \ + (HTONS(err_code - (IOT_TFTP_ERR_BASE+0x0040))) + +/**@brief Iterator for string list delimited with '\0'. */ +typedef struct +{ + char * p_start; /**< Pointer to the beginning of a string. */ + char * p_end; /**< Pointer to the end of a string. */ + struct curr_struct + { + char * p_key; /**< Pointer to the last, found key string. */ + char * p_value; /**< Pointer to the last, found value string. */ + } curr; +} option_iter_t; + +/**@brief Allowed states of a single TFTP instance. */ +typedef enum +{ + STATE_FREE = 0, /**< Start state, after calling UDP to allocate socket. */ + STATE_IDLE, /**< Socket is allocated, but not used. */ + STATE_CONNECTING_RRQ, /**< RRQ packet sent. Waiting for response. */ + STATE_CONNECTING_WRQ, /**< WRQ packet sent. Waiting for response. */ + STATE_SENDING, /**< Sending file and receiving ACK. */ + STATE_SEND_HOLD, /**< Sending held. Waiting for resume call. */ + STATE_RECEIVING, /**< Receiving file and sending ACK. */ + STATE_RECV_HOLD, /**< Receiving held. Waiting for resume call. */ + STATE_RECV_COMPLETE /**< State after receiving last DATA, before sending last ACK packet. There won't be another UDP event to emit IOT_TFTP_EVT_TRANSFER_GET_COMPLETE event, so next resume() should emit that event. */ +} tftp_state_t; + +/**@brief Internal TFTP instance structure. */ +typedef struct +{ + iot_tftp_trans_params_t init_params; /**< Connection parameters set during initialization. */ + iot_tftp_trans_params_t connect_params; /**< Negotiated Connection parameters. */ + udp6_socket_t socket; /**< UDP socket assigned to single instance. */ + tftp_state_t state; /**< Integer representing current state of an instance. */ + iot_tftp_callback_t callback; /**< User defined callback (passed inside initial parameters structure). */ + iot_file_t * p_file; /**< Pointer to destination/source file assigned in get/put call. */ + const char * p_path; /**< Path of the file on the remote node. */ + uint16_t block_id; /**< ID of last received/sent data block. */ + uint16_t src_tid; /**< UDP port used for sending information to the server. */ + uint16_t dst_tid; /**< UDP port on which all packets will be sent. At first - dst_port (see below), then reassigned. */ + uint16_t dst_port; /**< UDP port on which request packets will be sent. Usually DEFAULT_PORT. */ + const char * p_password; /**< Pointer to a constant string containing password passed inside Read/Write Requests. */ + ipv6_addr_t addr; /**< IPv6 server address. */ + iot_pbuffer_t * p_packet; /**< Reference to the temporary packet buffer. */ + uint8_t retries; /**< Number of already performed retries. */ + volatile iot_timer_time_in_ms_t request_timeout; /**< Number of milliseconds on which last request should be retransmitted. */ +} tftp_instance_t; + +SDK_MUTEX_DEFINE(m_tftp_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ +static tftp_instance_t m_instances[TFTP_MAX_INSTANCES]; /**< Array of allowed TFTP instances. */ + +/**@brief Function for finding free TFTP instance index. + * + * @param[out] p_index Index being found. + * + * @retval NRF_SUCCESS if passed instance was found, else NRF_ERROR_NO_MEM error code will + * be returned. + */ +static uint32_t find_free_instance(uint32_t * p_index) +{ + uint32_t index = 0; + + for (index = 0; index < TFTP_MAX_INSTANCES; index++) + { + if (m_instances[index].state == STATE_FREE) + { + *p_index = index; + + return NRF_SUCCESS; + } + } + + return (NRF_ERROR_NO_MEM | IOT_TFTP_ERR_BASE); +} + +/**@brief Function for resolving instance index by passed pointer. + * + * @param[in] p_tftp Pointer representing TFTP instance in user space. + * @param[out] p_index Index of passed TFTP instance. + * + * @retval NRF_SUCCESS if passed instance was found, else NRF_ERROR_INVALID_PARAM error code + * will be returned. + */ +static uint32_t find_instance(iot_tftp_t * p_tftp, uint32_t * p_index) +{ + if (*p_tftp > TFTP_MAX_INSTANCES) + { + return (NRF_ERROR_INVALID_PARAM | IOT_TFTP_ERR_BASE); + } + + *p_index = *p_tftp; + + return NRF_SUCCESS; +} + +/**@brief Function for notifying application of the TFTP events. + * + * @param[in] p_tftp TFTP instance. + * @param[in] p_evt Event description. + * + * @retval None. + */ +static void app_notify(iot_tftp_t * p_tftp, iot_tftp_evt_t * p_evt) +{ + uint32_t index; + uint32_t err_code; + + err_code = find_instance(p_tftp, &index); + if (err_code != NRF_SUCCESS) + { + return; + } + + if (m_instances[index].callback) + { + TFTP_MUTEX_UNLOCK(); + + + // Call handler of user request. + m_instances[index].callback(p_tftp, p_evt); + + TFTP_MUTEX_LOCK(); + } +} + +/**@brief Increment option iterator. + * + * @details The iterator will point to the next option or to p_end if it reaches the end. + * + * @param[in] p_iter Pointer to option iterator. + * + * @retval NRF_SUCCESS if iterator successfully moved to next option, else an error code indicating reason + * for failure. + */ +static uint32_t op_get_next(option_iter_t * p_iter) +{ + uint32_t key_length; + uint32_t value_length; + + NULL_PARAM_CHECK(p_iter->p_start); + NULL_PARAM_CHECK(p_iter->p_end); + NULL_PARAM_CHECK(p_iter->curr.p_key); + NULL_PARAM_CHECK(p_iter->curr.p_value); + + // If reached end. + if ((p_iter->curr.p_value == p_iter->p_end) || (p_iter->curr.p_key == p_iter->p_end)) + { + return (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE); + } + + key_length = strlen(p_iter->curr.p_key); + value_length = strlen(p_iter->curr.p_value); + + if ((p_iter->curr.p_value == p_iter->p_start) && (p_iter->curr.p_key == p_iter->p_start)) + { + // First call. Check if [start] + [string] fits before [end] reached. + // This statement just checks if there is '\0' between start and end (passing single string as input). + if (p_iter->curr.p_key + key_length < p_iter->p_end) + { + p_iter->curr.p_value = p_iter->curr.p_key + key_length + 1; + + return NRF_SUCCESS; + } + else + { + return (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE); + } + } + else if (p_iter->curr.p_value + value_length < p_iter->p_end) + { + p_iter->curr.p_key = p_iter->curr.p_value + value_length + 1; + p_iter->curr.p_value = p_iter->curr.p_key + strlen(p_iter->curr.p_key) + 1; + + if ((*p_iter->curr.p_key == '\0') || (*p_iter->curr.p_value == '\0')) // If string list finishes before the end of the buffer. + { + p_iter->curr.p_key = p_iter->p_end; + p_iter->curr.p_value = p_iter->p_end; + + return (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE); + } + + return NRF_SUCCESS; + } + else + { + p_iter->curr.p_key = p_iter->p_end; + p_iter->curr.p_value = p_iter->p_end; + + return (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE); + } +} + +/**@brief Set new (key, value) pair at the end of a string. + * + * @param[out] p_iter Pointer to iterator, which will be used to add (key, value) pair. + * @param[in] p_inp_key Pointer to the new key string. If p_key is NULL, then this function will insert just value. + * @param[in] p_inp_value Pointer to the new value string. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static __INLINE uint32_t op_set(option_iter_t * p_iter, const char * p_inp_key, const char * p_inp_value) +{ + char * p_last_key; + char * p_last_value; + + NULL_PARAM_CHECK(p_iter->p_start); + NULL_PARAM_CHECK(p_iter->p_end); + NULL_PARAM_CHECK(p_iter->curr.p_key); + NULL_PARAM_CHECK(p_iter->curr.p_value); + + p_last_key = p_iter->curr.p_key; + p_last_value = p_iter->curr.p_value; + + // Print appropriate trace log. + if (p_inp_key != NULL) + { + TFTP_TRC("Set option: %s with value: %s.", p_inp_key, p_inp_value); + } + else + { + TFTP_TRC("Set value: %s.", p_inp_value); + } + + // Set key & value pointers. + if ((p_iter->curr.p_key == p_iter->p_start) && (p_iter->curr.p_value == p_iter->p_start)) // Start condition. + { + if (p_inp_key != NULL) + { + p_iter->curr.p_value = p_iter->curr.p_key + strlen(p_inp_key) + 1; + } + else + { + p_iter->curr.p_value = p_iter->curr.p_key; // Insert only passed value. + p_iter->curr.p_key = p_iter->curr.p_value + strlen(p_iter->curr.p_value) + 1; // Just assign anything different that p_start and inside buffer. + } + } + else + { + p_iter->curr.p_key = p_iter->curr.p_value + strlen(p_iter->curr.p_value) + 1; // New key starts where last value ends. + + if (p_inp_key != NULL) + { + p_iter->curr.p_value = p_iter->curr.p_key + strlen(p_inp_key) + 1; // If key not null - new value starts where new key ends. + } + else + { + p_iter->curr.p_value = p_iter->curr.p_key; // Otherwise - value is placed at the key position. + } + } + + // Copy strings into set pointers. + if ((p_iter->curr.p_value + strlen(p_inp_value)) < p_iter->p_end) + { + if (p_inp_key != NULL) + { + memcpy(p_iter->curr.p_key, p_inp_key, strlen(p_inp_key) + 1); + } + memcpy(p_iter->curr.p_value, p_inp_value, strlen(p_inp_value) + 1); + } + else // If it is not possible to insert new key & value pair. + { + p_iter->curr.p_key = p_last_key; + p_iter->curr.p_value = p_last_value; + + TFTP_ERR("Unable to set option (size error)!"); + + return (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE); + } + + return NRF_SUCCESS; +} + +/**@brief Initializes new option iterator. + * + * @param[out] p_iter Pointer to iterator, which will be configured. + * @param[in] p_buf Pointer to the new string buffer which iterator will be modifying. + * @param[in] buf_len Length of passed buffer. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static __INLINE void op_init(option_iter_t * p_iter, char * p_buf, uint32_t buf_len) +{ + p_iter->p_start = p_buf; + p_iter->p_end = p_buf + buf_len; + p_iter->curr.p_key = p_buf; + p_iter->curr.p_value = p_buf; +} + +/**@brief: Converts string containing unsigned number into uint32_t. + * + * @param[in] p_str Input string. + * + * @retval Integer number equal to read value. Reading process skips all non-digit characters. + */ +static uint32_t str_to_uint(char * p_str) +{ + uint32_t len; + uint32_t ret_val = 0; + uint32_t mul = 1; + + if (p_str == NULL) + { + return 0; + } + + len = strlen(p_str); + + while (len) + { + len--; + + if ((p_str[len] >= '0') && (p_str[len] <= '9')) // Skip unsupported characters. + { + ret_val += mul * (p_str[len] - '0'); + mul *= 10; + } + } + + return ret_val; +} + +/**@brief: Converts unsigned number into string. + * + * @param[in] number Input number. + * @param[out] p_str Pointer to the output string. + * @param[in] len Length of the passed output string buffer. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t uint_to_str(uint32_t number, char * p_str, uint16_t len) +{ + uint32_t i = 0; + uint32_t temp = number; + + if (len == 0) + { + return NRF_ERROR_INVALID_LENGTH; + } + + // Check how many characters will be needed. + if (temp == 0) + { + i = 1; + } + + while (temp) + { + i++; + temp /= 10; + } + + // Set null character and check length. + if (i + 1 > len) + { + p_str[0] = '\0'; + + return NRF_ERROR_INVALID_LENGTH; + } + + p_str[i] = '\0'; + + // Set digits. + while (i--) + { + p_str[i] = '0' + number % 10; + number /= 10; + } + + return NRF_SUCCESS; +} + +/**@brief Compare strings in a case insensitive way. + * + * @param[in] p_str1 Pointer to the first string. + * @param[in] p_str2 Pointer to the second String. + * + * @retval If strings are equal returns 0, otherwise number of common characters. + */ +static uint32_t strcmp_ci(char * p_str1, char* p_str2) +{ + uint32_t min_len = 0; + uint32_t str1_len; + uint32_t str2_len; + uint32_t i = 0; + + str1_len = strlen(p_str1); + str2_len = strlen(p_str2); + + min_len = str1_len; + + if (str2_len < str1_len) + { + min_len = str2_len; + } + + for (i = 0; i < min_len; i++) + { + char c1 = ((p_str1[i] >= 'a' && p_str1[i] <= 'z') ? p_str1[i] + 'A' - 'a' : p_str1[i]); + char c2 = ((p_str2[i] >= 'a' && p_str2[i] <= 'z') ? p_str2[i] + 'A' - 'a' : p_str2[i]); + + if (c1 != c2) + { + return i + 1; + } + } + + if (str1_len != str2_len) + { + return i + 1; + } + + return 0; +} + +/**@brief Allocates p_buffer and fills in common fields. + * + * @param[in] type First field describing packet type. + * @param[in] id Second field (Block ID / Error Code). + * @param[out] pp_buffer Sets pointer to the newly allocated buffer. + * @param[in] payload_len Length of payload (additional fields / data). + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t compose_packet(uint16_t type, + uint16_t id, + iot_pbuffer_t ** pp_buffer, + uint32_t payload_len) +{ + uint32_t err_code; + iot_pbuffer_alloc_param_t buffer_param; + iot_pbuffer_t * p_buffer; + uint32_t byte_index; + + memset(&buffer_param, 0, sizeof(iot_pbuffer_alloc_param_t)); + buffer_param.length = TFTP_HEADER_SIZE + TFTP_BLOCK_ID_SIZE + payload_len; + buffer_param.type = UDP6_PACKET_TYPE; + buffer_param.flags = PBUFFER_FLAG_DEFAULT; + + err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + memset(p_buffer->p_payload, 0, buffer_param.length); + byte_index = 0; + + // Insert type opcode. + byte_index += uint16_encode(HTONS(type), &p_buffer->p_payload[byte_index]); + + if (type == TYPE_ERR) + { + // Insert err code. + byte_index += uint16_encode(CONVERT_TO_TFTP_ERROR(id), &p_buffer->p_payload[byte_index]); + } + else + { + // Insert block ID. + byte_index += uint16_encode(HTONS(id), &p_buffer->p_payload[byte_index]); + } + + *pp_buffer = p_buffer; + + return NRF_SUCCESS; +} + +/**@brief Reset instance request timer. + * + * @param[in] index Index of pending instance. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t retr_timer_reset(uint32_t index) +{ + uint32_t err_code; + iot_timer_time_in_ms_t wall_clock_value; + + // Get wall clock time. + err_code = iot_timer_wall_clock_get(&wall_clock_value); + + if (err_code == NRF_SUCCESS) + { + m_instances[index].request_timeout = wall_clock_value + m_instances[index].connect_params.next_retr * 1000; + } + + return err_code; +} + +/**@brief Function for checking if retransmission time of TFTP instance request has been expired. + * + * @param[in] index Index of pending instance. + * + * @retval True if timer has been expired, False otherwise. + */ +static bool instance_timer_is_expired(uint32_t index) +{ + uint32_t err_code; + iot_timer_time_in_ms_t wall_clock_value; + + // Get wall clock time. + err_code = iot_timer_wall_clock_get(&wall_clock_value); + + if (err_code == NRF_SUCCESS) + { + if (wall_clock_value >= m_instances[index].request_timeout) + { + return true; + } + } + + return false; +} + +/**@brief Sets all instance values to defaults. */ +static void instance_reset(uint32_t index) +{ + m_instances[index].state = STATE_FREE; + m_instances[index].init_params.next_retr = 0; + m_instances[index].init_params.block_size = TFTP_DEFAULT_BLOCK_SIZE; + m_instances[index].connect_params.next_retr = 0; + m_instances[index].connect_params.block_size = TFTP_DEFAULT_BLOCK_SIZE; + m_instances[index].p_file = NULL; + m_instances[index].block_id = 0; + m_instances[index].p_packet = NULL; + m_instances[index].dst_port = TFTP_DEFAULT_PORT; + m_instances[index].dst_tid = TFTP_DEFAULT_PORT; + m_instances[index].retries = 0; + m_instances[index].request_timeout = 0; + m_instances[index].callback = NULL; + m_instances[index].src_tid = 0; + m_instances[index].p_password = NULL; + memset(&m_instances[index].addr, 0, sizeof(ipv6_addr_t)); + memset(&m_instances[index].socket, 0, sizeof(udp6_socket_t)); +} + +/**@brief This function creates error packet for specified TFTP instance. + * + * @param[in] index Index of TFTP instance. + * @param[in] p_err_evt Event data structure (message and code). + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t send_err_msg(uint32_t index, iot_tftp_evt_err_t * p_err_evt) +{ + iot_pbuffer_t * p_buffer; + uint8_t * p_resp_packet; + uint32_t msg_len = 0; + uint16_t byte_index = 0; + uint32_t err_code; + + TFTP_TRC("Send ERROR packet."); + + if (p_err_evt->p_msg != NULL) + { + msg_len = strlen(p_err_evt->p_msg) + 1; + } + + err_code = compose_packet(TYPE_ERR, p_err_evt->code, &p_buffer, msg_len); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + p_resp_packet = p_buffer->p_payload; + byte_index = TFTP_HEADER_SIZE + TFTP_ERR_CODE_SIZE; + + if (p_err_evt->p_msg != NULL) + { + memcpy(&p_resp_packet[byte_index], p_err_evt->p_msg, msg_len); + byte_index += msg_len; + } + + p_buffer->length = byte_index; + + TFTP_TRC("Send packet to UDP module."); + + UNUSED_VARIABLE(retr_timer_reset(index)); + + err_code = udp6_socket_sendto(&m_instances[index].socket, + &m_instances[index].addr, + m_instances[index].dst_tid, + p_buffer); + + TFTP_TRC("Recv code: %08lx.", err_code); + + return err_code; +} + +/**@brief This function creeates error packet for TID error, not being found. + * + * @param[in] p_socket Socket from which error message is sent. + * @param[in] p_addr IPv6 Address to where error message is sent. + * @param[in] tid Erronous TID from the sender. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t send_err_tid(const udp6_socket_t * p_socket, const ipv6_addr_t * p_addr, uint16_t tid) +{ + iot_pbuffer_t * p_buffer; + uint32_t msg_len = 0; + uint16_t byte_index = 0; + uint32_t err_code; + + TFTP_TRC("Send TID ERROR packet."); + + err_code = compose_packet(TYPE_ERR, ERR_INVALID_TID, &p_buffer, msg_len); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + byte_index = TFTP_HEADER_SIZE + TFTP_ERR_CODE_SIZE; + p_buffer->length = byte_index; + + TFTP_TRC("Send packet to UDP module."); + + err_code = udp6_socket_sendto(p_socket, p_addr, tid, p_buffer); + + TFTP_TRC("Recv code: %08lx.", err_code); + + return err_code; +} + +/**@brief Sends ACK or next data chunk (block) after calling user callback or when hold timer expires. + * + * @param[in] p_tftp Pointer to the TFTP instance (from user space). + * @param[in] p_evt Pointer to the event structure. Used for sending error messages. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t send_response(iot_tftp_t * p_tftp) +{ + uint32_t index; + uint32_t err_code; + + TFTP_TRC("Send packet."); + + err_code = find_instance(p_tftp, &index); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Cannot find instance (send)!"); + + return err_code; + } + + switch (m_instances[index].state) + { + case STATE_IDLE: + // Server should go to the CONNECTING state (request received). + TFTP_TRC("Inside IDLE state (send). "); + return NRF_SUCCESS; + + case STATE_SENDING: + case STATE_RECEIVING: + case STATE_SEND_HOLD: + case STATE_RECV_HOLD: + case STATE_RECV_COMPLETE: + // Send DATA/ACK packet. + TFTP_TRC("Send packet to UDP module."); + + UNUSED_VARIABLE(retr_timer_reset(index)); + err_code = udp6_socket_sendto(&m_instances[index].socket, + &m_instances[index].addr, + m_instances[index].dst_tid, + m_instances[index].p_packet); + TFTP_TRC("Recv code: %08lx.", err_code); + return err_code; + + default: + TFTP_ERR("Invalid state (send)!"); + return (NRF_ERROR_INVALID_STATE | IOT_TFTP_ERR_BASE); + } +} + + + +/**@brief Aborts TFTP client ongoing procedure. + * + * @param[in] index Index of TFTP instance. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +void instance_abort(uint32_t index) +{ + uint32_t internal_err; + + switch (m_instances[index].state) + { + case STATE_SEND_HOLD: + case STATE_RECV_HOLD: + // Free pbuffer. + internal_err = iot_pbuffer_free(m_instances[index].p_packet, true); + if (internal_err != NRF_SUCCESS) + { + TFTP_ERR("Cannot free pbuffer - %p", m_instances[index].p_packet); + } + + // Close file. + internal_err = iot_file_fclose(m_instances[index].p_file); + if (internal_err != NRF_SUCCESS) + { + TFTP_ERR("Cannot close file - %p", m_instances[index].p_file); + } + + break; + case STATE_SENDING: + case STATE_RECEIVING: + // Close file. + internal_err = iot_file_fclose(m_instances[index].p_file); + if (internal_err != NRF_SUCCESS) + { + TFTP_ERR("Cannot close file - %p", m_instances[index].p_file); + } + break; + case STATE_CONNECTING_RRQ: + case STATE_CONNECTING_WRQ: + case STATE_IDLE: + case STATE_FREE: + default: + break; + } + + TFTP_TRC("Reset instance %ld.", index); + + m_instances[index].state = STATE_IDLE; + m_instances[index].block_id = 0; + m_instances[index].dst_tid = m_instances[index].dst_port; + m_instances[index].retries = 0; + m_instances[index].request_timeout = 0; + memcpy(&m_instances[index].connect_params, + &m_instances[index].init_params, + sizeof(iot_tftp_trans_params_t)); +} + + +/**@brief Generates event for application. + * + * @param[in] index Index of TFTP instance. + * @param[in] evt_id Id of generated event. + * @param[in] p_param Pointer to event parameter union. + */ +static void handle_evt(uint32_t index, iot_tftp_evt_id_t evt_id, iot_tftp_evt_param_t * p_param) +{ + uint32_t internal_err; + iot_tftp_evt_t evt; + + memset(&evt, 0, sizeof(evt)); + evt.id = evt_id; + evt.p_file = m_instances[index].p_file; + + if (p_param != NULL) + { + evt.param = * p_param; + } + + if (evt_id == IOT_TFTP_EVT_ERROR) + { + uint32_t err_code = evt.param.err.code; + + TFTP_TRC("Raise an ERROR event."); + + if ((err_code & IOT_TFTP_ERR_BASE) == IOT_TFTP_ERR_BASE) + { + evt.param.err.code = CONVERT_TO_TFTP_ERROR(err_code); + + internal_err = send_err_msg(index, &evt.param.err); + if (internal_err != NRF_SUCCESS) + { + TFTP_TRC("Cannot send error message to peer %08lx.", internal_err); + } + } + + // Restore error code. + evt.param.err.code = err_code; + + // Return to IDLE and notify. + instance_abort(index); + + app_notify(&index, &evt); + } + else if ((evt_id == IOT_TFTP_EVT_TRANSFER_GET_COMPLETE) || + (evt_id == IOT_TFTP_EVT_TRANSFER_PUT_COMPLETE)) + { + TFTP_TRC("Raise TRANSFER COMPLETE event."); + + if (m_instances[index].state == STATE_RECV_HOLD) + { + TFTP_TRC("Holding last ACK transfer."); + m_instances[index].state = STATE_RECV_COMPLETE; + } + else if (m_instances[index].state != STATE_RECV_COMPLETE) + { + // Skip into IDLE state. + instance_abort(index); + + app_notify(&index, &evt); + } + } + else if (evt_id == IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED) + { + app_notify(&index, &evt); + } +} + +/**@brief Generates error event for application. + * + * @param[in] index Index of TFTP instance. + * @param[in] err_code Code of error event. + * @param[in] p_msg Character string containing error message. + */ +static void handle_evt_err(uint32_t index, uint32_t err_code, char * p_msg) +{ + iot_tftp_evt_param_t evt_param; + + memset(&evt_param, 0, sizeof(evt_param)); + + evt_param.err.code = err_code; + evt_param.err.p_msg = p_msg; + evt_param.err.size_transfered = m_instances[index].block_id * m_instances[index].connect_params.block_size; + + handle_evt(index, IOT_TFTP_EVT_ERROR, &evt_param); +} + +/**@brief: Find instance number by Transmission ID (UDP source port). + * + * @param[in] port UDP port number on which new message was received. + * @param[out] p_index Index of found TFTP instance assigned to the passed UDP port. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static __INLINE uint32_t find_instance_by_tid(uint16_t port, uint32_t * p_index) +{ + uint32_t index; + + for (index = 0; index < TFTP_MAX_INSTANCES; index++) + { + if (m_instances[index].src_tid == port) + { + break; + } + } + + if (index == TFTP_MAX_INSTANCES) + { + return (NRF_ERROR_NOT_FOUND | IOT_TFTP_ERR_BASE); + } + + *p_index = index; + + return NRF_SUCCESS; +} + +/**@brief: Negotiation procedure. This function skips through input option string and modifies instance parameters according to negotiated values. + * + * @param[in] p_iter Pointer to iterator configured to parse RQ/OACK packet. + * @param[out] p_instance Pointer to the instance, which parameters should be negotiated. + * + * @retval NRF_SUCCESS on successful execution of procedure, else TFTP_OPTION_REJECT error indicating + * that server/client requests unsupported option values. + */ +static uint32_t option_negotiate(option_iter_t * p_iter, tftp_instance_t * p_instance) +{ + uint32_t err_code; + bool op_size_set = false; + bool op_blksize_set = false; + bool op_time_set = false; + + TFTP_TRC("Negotiate options:"); + + if (p_iter != NULL) // If NULL passed - reset option values to those defined by RFC. + { + UNUSED_VARIABLE(op_get_next(p_iter)); + + while (p_iter->curr.p_key != p_iter->p_end) + { + if (strcmp_ci(p_iter->curr.p_key, OPTION_TIMEOUT) == 0) + { + if (p_instance->init_params.next_retr != 0) + { + uint16_t server_time = str_to_uint(p_iter->curr.p_value); + + if (server_time < p_instance->init_params.next_retr) // Take minimum. + { + p_instance->connect_params.next_retr = server_time; + } + else + { + p_instance->connect_params.next_retr = p_instance->init_params.next_retr; + } + + op_time_set = true; + + TFTP_TRC("TIMEOUT: %ld", p_instance->connect_params.next_retr); + } + } + else if (strcmp_ci(p_iter->curr.p_key, OPTION_SIZE) == 0) + { + if (p_instance->p_file != NULL) + { + uint32_t file_size = str_to_uint(p_iter->curr.p_value); + + err_code = iot_file_fopen(p_instance->p_file, file_size); + if (err_code != NRF_SUCCESS) + { + TFTP_TRC(" TSIZE: REJECT!"); + return TFTP_OPTION_REJECT; + } + + op_size_set = true; + TFTP_TRC(" TSIZE: %ld", file_size); + } + } + else if (strcmp_ci(p_iter->curr.p_key, OPTION_BLKSIZE) == 0) + { + uint16_t block_size = str_to_uint(p_iter->curr.p_value); + op_blksize_set = true; + + if (p_instance->init_params.block_size >= block_size) + { + p_instance->connect_params.block_size = block_size; + } + else + { + TFTP_TRC("BLKSIZE: REJECT!"); + return TFTP_OPTION_REJECT; + } + + TFTP_TRC("BLKSIZE: %d", p_instance->connect_params.block_size); + } + else if ((strlen(p_iter->curr.p_key) > 0) && (p_iter->curr.p_value == p_iter->p_end)) + { + // Password option. + TFTP_TRC("PASSWORD FOUND: %s", p_iter->curr.p_key); + p_iter->curr.p_key = p_iter->p_end; + } + else + { + TFTP_TRC("UNKNOWN OPTION"); + } + + UNUSED_VARIABLE(op_get_next(p_iter)); + } + } + + // Set values of not negotiated options. + if (!op_size_set && p_instance->p_file != NULL) + { + err_code = iot_file_fopen(p_instance->p_file, 0);// Open with default file size (not known). + + if (err_code != NRF_SUCCESS) + { + TFTP_TRC("TSIZE: REJECT!"); + return TFTP_OPTION_REJECT; + } + + TFTP_TRC("TSIZE: %ld", p_instance->p_file->file_size); + } + + if (!op_blksize_set) + { + if ((p_instance->init_params.block_size < TFTP_DEFAULT_BLOCK_SIZE) && + (p_instance->init_params.block_size != 0)) + { + TFTP_TRC("BLKSIZE: REJECT!"); + return TFTP_OPTION_REJECT; + } + else + { + p_instance->connect_params.block_size = TFTP_DEFAULT_BLOCK_SIZE; + } + + TFTP_TRC("BLKSIZE: %d", p_instance->connect_params.block_size); + } + + if (!op_time_set) + { + p_instance->connect_params.next_retr = p_instance->init_params.next_retr; + + TFTP_TRC("TIMEOUT: %ld", p_instance->connect_params.next_retr); + } + + return NRF_SUCCESS; +} + + + +/**@brief This function holds ongoing transmission of TFTP. + * + * @param[in] index Index of TFTP instance. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t transfer_hold(uint32_t index) +{ + if (m_instances[index].state == STATE_SENDING) + { + m_instances[index].state = STATE_SEND_HOLD; + } + else if (m_instances[index].state == STATE_RECEIVING) + { + m_instances[index].state = STATE_RECV_HOLD; + } + else if (m_instances[index].state == STATE_RECV_COMPLETE) + { + m_instances[index].state = STATE_RECV_COMPLETE; + } + else + { + TFTP_ERR("Hold called in invalid state."); + + return (NRF_ERROR_INVALID_STATE | IOT_TFTP_ERR_BASE); + } + + return NRF_SUCCESS; +} + + +/**@brief This function resumes ongoing transmission of TFTP. + * + * @param[in] index Index of TFTP instance. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t transfer_resume(uint32_t index) +{ + uint32_t err_code = NRF_SUCCESS; + + if ((m_instances[index].state != STATE_SEND_HOLD) && + (m_instances[index].state != STATE_RECV_HOLD) && + (m_instances[index].state != STATE_RECV_COMPLETE)) + { + TFTP_ERR("Failed due to invalid state."); + TFTP_EXIT(); + return (NRF_ERROR_INVALID_STATE | IOT_TFTP_ERR_BASE); + } + + err_code = send_response(&index); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Failed to send packet after resume."); + } + + if (m_instances[index].state == STATE_SEND_HOLD) + { + m_instances[index].state = STATE_SENDING; + } + else if (m_instances[index].state == STATE_RECV_HOLD) + { + m_instances[index].state = STATE_RECEIVING; + } + else if (m_instances[index].state == STATE_RECV_COMPLETE) + { + m_instances[index].state = STATE_RECEIVING; + TFTP_ERR("Complete due to STATE_RECV_COMPLETE state."); + handle_evt(index, IOT_TFTP_EVT_TRANSFER_GET_COMPLETE, NULL); + } + + return err_code; +} + + +/**@brief This function creates ACK packet for specified TFTP instance. + * + * @param[in] index Index of TFTP instance. + * @param[in] block_id Data block ID to be acknowledged. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t create_ack_packet(uint32_t index, uint16_t block_id) +{ + uint32_t err_code; + + TFTP_ENTRY(); + + // Reuse p_packet pointer for a new (response) packet. Previous one will be automatically freed by IPv6 module. + err_code = compose_packet(TYPE_ACK, + block_id, + &m_instances[index].p_packet, + 0); + + return err_code; +} + +/**@brief This function creates data packet for specified TFTP instance. + * + * @param[in] index Index of TFTP instance. + * @param[in] block_id Data block ID to be sent. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t create_data_packet(uint32_t index, uint16_t block_id) +{ + uint32_t err_code; + uint32_t internal_err; + uint32_t cursor; + uint32_t byte_index; + iot_pbuffer_t * p_buffer = NULL; + + TFTP_ENTRY(); + + if (m_instances[index].p_file == NULL) + { + TFTP_ERR("[TFTP]: Error while sending response. Reason: Missing file to send.\r\n"); + return NRF_ERROR_INVALID_DATA; + } + + // If ACK block ID doesn't match last sent packet number. + if (m_instances[index].block_id != block_id) + { + // fseek on file to move to the right point. If fseek returns error - whole transmission will be dropped. + err_code = iot_file_fseek(m_instances[index].p_file, + (block_id) * m_instances[index].connect_params.block_size); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Unable to fseek on input data file!"); + } + else + { + m_instances[index].block_id = block_id + 1; + } + } + else + { + m_instances[index].block_id = block_id + 1; + } + + cursor = (m_instances[index].block_id - 1) * m_instances[index].connect_params.block_size; + + // If previous DATA packet wasn't fully filled. + if (cursor > m_instances[index].p_file->file_size) + { + TFTP_TRC("Transfer complete. Don't send data."); + handle_evt(index, IOT_TFTP_EVT_TRANSFER_PUT_COMPLETE, NULL); + return NRF_SUCCESS; + } + else if (cursor + m_instances[index].connect_params.block_size > + m_instances[index].p_file->file_size) // If current sendto operation will send all remaining data. + { + TFTP_TRC("Send last data packet."); + err_code = compose_packet(TYPE_DATA, + m_instances[index].block_id, + &p_buffer, + m_instances[index].p_file->file_size - cursor); + } + else + { + TFTP_TRC("Send regular data packet."); + err_code = compose_packet(TYPE_DATA, + m_instances[index].block_id, + &p_buffer, + m_instances[index].connect_params.block_size); + } + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + TFTP_TRC("Created packet:"); + TFTP_TRC("length: %ld", p_buffer->length); + TFTP_TRC(" type: %d", p_buffer->p_payload[0] * 256 + p_buffer->p_payload[1]); + TFTP_TRC(" ID: %d", p_buffer->p_payload[2] * 256 + p_buffer->p_payload[3]); + + byte_index = TFTP_HEADER_SIZE + TFTP_BLOCK_ID_SIZE; + + // Save reference to correctly filled packet buffer. + m_instances[index].p_packet = p_buffer; + + if (p_buffer->length - TFTP_HEADER_SIZE - TFTP_BLOCK_ID_SIZE != 0) + { + TFTP_MUTEX_UNLOCK(); + + // Hold transfer. + internal_err = transfer_hold(index); + if (internal_err != NRF_SUCCESS) + { + TFTP_ERR("Error while holding the transfer. Reason: %08lx.", internal_err); + } + + err_code = iot_file_fread(m_instances[index].p_file, + &(p_buffer->p_payload[byte_index]), + p_buffer->length - TFTP_HEADER_SIZE - TFTP_BLOCK_ID_SIZE); + + // Unlock instance if file has not assigned callback (probably no needs more time to perform read/write). + if (m_instances[index].p_file->p_callback == NULL) + { + internal_err = transfer_resume(index); + if (internal_err != NRF_SUCCESS) + { + TFTP_ERR("Error while resuming the transfer. Reason: %08lx.", internal_err); + } + } + TFTP_MUTEX_LOCK(); + } + else + { + // TFTP is going to send empty data packet, so file callback won't be called. + internal_err = send_response(&index); + if (internal_err != NRF_SUCCESS) + { + TFTP_ERR("Error while sending response. Reason: %08lx.", internal_err); + } + } + + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Failed to save received data (fread)!"); + handle_evt_err(index, TFTP_ACCESS_DENIED, ACCESS_ERROR_MSG); + } + + return err_code; +} + + +/**@brief Callback handler to receive data on the UDP port. + * + * @param[in] p_socket Socket identifier. + * @param[in] p_ip_header IPv6 header containing source and destination addresses. + * @param[in] p_udp_header UDP header identifying local and remote endpoints. + * @param[in] process_result Result of data reception, there could be possible errors like + * invalid checksum etc. + * @param[in] p_rx_packet Packet buffer containing the received data packet. + * + * @retval NRF_SUCCESS Indicates received data was handled successfully, else an an + * error code indicating reason for failure.. + */ +static uint32_t client_process(const udp6_socket_t * p_socket, + const ipv6_header_t * p_ip_header, + const udp6_header_t * p_udp_header, + uint32_t process_result, + iot_pbuffer_t * p_rx_packet) +{ + uint32_t index; + iot_tftp_evt_t evt; + uint8_t * p_new_packet; + uint32_t byte_index; + uint32_t err_code; + uint32_t internal_err; + uint16_t packet_opcode; + option_iter_t oack_iter; + uint16_t recv_block_id; + + TFTP_ENTRY(); + + TFTP_MUTEX_LOCK(); + + err_code = find_instance_by_tid(p_udp_header->destport, &index); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Unable to find TFTP instance associated with given UDP port."); + + // Send TID error to the server. + err_code = send_err_tid(p_socket, &p_ip_header->srcaddr, p_udp_header->destport); + + TFTP_EXIT(); + TFTP_MUTEX_UNLOCK(); + + return err_code; + } + + // Check UDP status code. + if (process_result != NRF_SUCCESS) + { + TFTP_ERR("UDP error!"); + + evt.id = IOT_TFTP_EVT_ERROR; + evt.param.err.code = process_result; + evt.param.err.p_msg = UDP_ERROR_MSG; + + // Call user callback (inform about error). + app_notify(&index, &evt); + + TFTP_EXIT(); + + TFTP_MUTEX_UNLOCK(); + + return process_result; + } + + // Check packet length. + if (p_rx_packet->length < TFTP_HEADER_SIZE + TFTP_BLOCK_ID_SIZE) + { + TFTP_ERR("Invalid packet length!"); + + evt.id = IOT_TFTP_EVT_ERROR; + evt.param.err.code = TFTP_INVALID_PACKET; + evt.param.err.p_msg = (char *)"Invalid packet length!"; + + app_notify(&index, &evt); + + TFTP_EXIT(); + + TFTP_MUTEX_UNLOCK(); + + return evt.param.err.code; + } + + // Read received packet type. + p_new_packet = p_rx_packet->p_payload; + packet_opcode = uint16_decode(p_new_packet); + packet_opcode = NTOHS(packet_opcode); + byte_index = TFTP_HEADER_SIZE; + + if ((m_instances[index].state == STATE_SEND_HOLD) || + (m_instances[index].state == STATE_RECV_HOLD) || + (m_instances[index].state == STATE_IDLE)) + { + TFTP_TRC("Ignore packets in HOLD/IDLE states."); + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return NRF_SUCCESS; // Ignore retransmission of other side. + } + + switch (packet_opcode) + { + case TYPE_OACK: + if ((m_instances[index].state != STATE_CONNECTING_RRQ) && + (m_instances[index].state != STATE_CONNECTING_WRQ) && + (m_instances[index].state != STATE_IDLE && m_instances[index].retries > 0)) + { + TFTP_ERR("Invalid TFTP instance state!"); + break; + } + + op_init(&oack_iter, + (char *)&p_new_packet[byte_index], + p_rx_packet->length - TFTP_HEADER_SIZE); // Options uses whole packet except opcode. + + TFTP_TRC("Received OACK."); + err_code = option_negotiate(&oack_iter, &m_instances[index]); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Failed to negotiate options!"); + handle_evt_err(index, TFTP_OPTION_REJECT, OPTION_ERROR_MESSAGE); + break; + } + + // Set server transmission id. + m_instances[index].dst_tid = p_udp_header->srcport; + + if (m_instances[index].state == STATE_CONNECTING_RRQ) + { + m_instances[index].p_packet = p_rx_packet; + m_instances[index].state = STATE_RECEIVING; + + err_code = create_ack_packet(index, 0); + + if (err_code == NRF_SUCCESS) + { + err_code = send_response(&index); + } + } + else if (m_instances[index].state == STATE_CONNECTING_WRQ) + { + m_instances[index].state = STATE_SENDING; + + err_code = create_data_packet(index, 0); + } + else + { + TFTP_ERR("Incorrect state to receive OACK!"); + } + + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Failed to create packet!"); + handle_evt_err(index, err_code, NULL); + } + break; + + case TYPE_ACK: + recv_block_id = uint16_decode(&p_new_packet[byte_index]); + recv_block_id = NTOHS(recv_block_id); + + if (m_instances[index].state == STATE_CONNECTING_WRQ) + { + TFTP_TRC("Options not supported. Received ACK."); + err_code = option_negotiate(NULL, &m_instances[index]); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Failed to negotiate default options!"); + handle_evt_err(index, TFTP_OPTION_REJECT, OPTION_ERROR_MESSAGE); + break; + } + + // Set server transmission id. + m_instances[index].dst_tid = p_udp_header->srcport; + + // Set instance state. + m_instances[index].state = STATE_SENDING; + m_instances[index].block_id = 0; + } + + if (m_instances[index].state == STATE_SENDING || m_instances[index].state == STATE_RECEIVING) + { + if (recv_block_id == m_instances[index].block_id) + { + m_instances[index].retries = 0; + } + TFTP_TRC("Received ACK. Send block %4d of %ld.", m_instances[index].block_id + 1, + CEIL_DIV(m_instances[index].p_file->file_size, m_instances[index].connect_params.block_size) + + ((m_instances[index].p_file->file_size % m_instances[index].connect_params.block_size == 0) ? 0 : 1)); + + err_code = create_data_packet(index, recv_block_id); + if ((err_code != (NRF_ERROR_DATA_SIZE | IOT_TFTP_ERR_BASE)) && (err_code != NRF_SUCCESS)) + { + TFTP_ERR("Failed to create data packet."); + handle_evt_err(index, err_code, NULL); + } + } + else + { + TFTP_ERR("Invalid state to receive ACK packet."); + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return NRF_SUCCESS; + } + break; + + case TYPE_DATA: + recv_block_id = uint16_decode(&p_new_packet[byte_index]); + recv_block_id = NTOHS(recv_block_id); + byte_index += TFTP_BLOCK_ID_SIZE; + + if (m_instances[index].state == STATE_CONNECTING_RRQ) + { + TFTP_TRC("Options not supported. Received DATA."); + err_code = option_negotiate(NULL, &m_instances[index]); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Failed to set default options."); + handle_evt_err(index, TFTP_OPTION_REJECT, OPTION_ERROR_MESSAGE); + break; + } + + if (recv_block_id == 1) // Received first data block. + { + m_instances[index].block_id = 0; + m_instances[index].state = STATE_RECEIVING; + } + + // Set server transmission id. + m_instances[index].dst_tid = p_udp_header->srcport; + } + + if (m_instances[index].state == STATE_RECEIVING) + { + TFTP_TRC("Received DATA."); + + m_instances[index].p_packet = p_rx_packet; + + if (recv_block_id == m_instances[index].block_id + 1) + { + TFTP_TRC("Received next DATA (n+1)."); + + m_instances[index].retries = 0; + + err_code = create_ack_packet(index, m_instances[index].block_id + 1); + + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Failed to create ACK packet!"); + handle_evt_err(index, err_code, NULL); + break; + } + } + else + { + TFTP_TRC("Skip current DATA packet. Try to request proper block ID by sending ACK."); + + err_code = create_ack_packet(index, m_instances[index].block_id); + + if (err_code == NRF_SUCCESS) + { + err_code = send_response(&index); + } + else + { + TFTP_ERR("Failed to create ACK packet."); + handle_evt_err(index, err_code, NULL); + } + } + + // Check if payload size is smaller than defined block size. + if ((p_rx_packet->length - TFTP_BLOCK_ID_SIZE - TFTP_HEADER_SIZE) < + m_instances[index].connect_params.block_size) + { + m_instances[index].state = STATE_RECV_COMPLETE; + } + else if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Failed to create ACK packet."); + handle_evt_err(index, err_code, NULL); + break; + } + + TFTP_TRC("Send block %4d of %ld ACK.", m_instances[index].block_id, + m_instances[index].p_file->file_size / m_instances[index].connect_params.block_size); + + if (recv_block_id == m_instances[index].block_id + 1) + { + m_instances[index].block_id = recv_block_id; + TFTP_MUTEX_UNLOCK(); + + if (p_rx_packet->length - byte_index > 0) + { + iot_tftp_evt_param_t evt_param; + memset(&evt_param, 0, sizeof(evt_param)); + evt_param.data_received.p_data = &p_new_packet[byte_index]; + evt_param.data_received.size = p_rx_packet->length - byte_index; + + internal_err = transfer_hold(index); + if (internal_err != NRF_SUCCESS) + { + TFTP_ERR("Error while holding the transfer. Reason: %08lx.", internal_err); + } + + if (m_instances[index].p_file != NULL) + { + err_code = iot_file_fwrite(m_instances[index].p_file, + evt_param.data_received.p_data, + evt_param.data_received.size); + } + + handle_evt(index, IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED, &evt_param); + + // Unlock instance if file has not assigned callback (probably not needs more time to perform read/write). + if (m_instances[index].p_file == NULL || m_instances[index].p_file->p_callback == NULL) + { + internal_err = transfer_resume(index); + if (internal_err != NRF_SUCCESS) + { + TFTP_ERR("Error while resuming the transfer. Reason: %08lx.", internal_err); + } + } + } + else + { + if (m_instances[index].p_file != NULL) + { + err_code = iot_file_fclose(m_instances[index].p_file); + } + + internal_err = send_response(&index); + if (internal_err != NRF_SUCCESS) + { + TFTP_ERR("Error while sending response. Reason: %08lx.", internal_err); + } + + TFTP_ERR("Complete due to packet length. (%ld: %ld)", p_rx_packet->length, byte_index); + m_instances[index].state = STATE_RECEIVING; + handle_evt(index, IOT_TFTP_EVT_TRANSFER_GET_COMPLETE, NULL); + } + + TFTP_MUTEX_LOCK(); + + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Failed to save received data (fwrite)!"); + handle_evt_err(index, TFTP_ACCESS_DENIED, ACCESS_ERROR_MSG); + break; + } + } + } + else + { + TFTP_ERR("Invalid state to receive DATA packet."); + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return NRF_SUCCESS; + } + break; + + case TYPE_ERR: + recv_block_id = uint16_decode(&p_new_packet[byte_index]); + recv_block_id = NTOHS(recv_block_id); + byte_index += TFTP_ERR_CODE_SIZE; + + TFTP_ERR("Received error packet!"); + + evt.id = IOT_TFTP_EVT_ERROR; + evt.param.err.code = CONVERT_TO_IOT_ERROR(recv_block_id); + + if (p_rx_packet->length > TFTP_HEADER_SIZE + TFTP_ERR_CODE_SIZE) + { + evt.param.err.p_msg = (char *) &p_new_packet[byte_index]; + p_new_packet[p_rx_packet->length-1] = '\0'; + } + + app_notify(&index, &evt); + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + if (evt.param.err.code != TFTP_INVALID_TID) + { + instance_abort(index); + } + + return evt.param.err.code; + + default: + TFTP_ERR("Invalid TFTP packet opcode!"); + handle_evt_err(index, TFTP_INVALID_PACKET, NULL); + break; + } + + TFTP_EXIT(); + + return err_code; +} + +/**@brief Count how many bytes are required to store options inside request packet. + * + * @param[in] index Index of TFTP instance. + * @param[in] type Type of TFTP request. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t count_options_length(uint32_t index, uint32_t type) +{ + uint32_t op_length = 0; + char next_retr_str[NEXT_RETR_MAX_LENGTH]; + char block_size_str[BLKSIZE_MAX_LENGTH]; + char file_size_str[FILE_SIZE_MAX_LENGTH]; + + if ((m_instances[index].init_params.next_retr > 0) && + (m_instances[index].init_params.next_retr < 256)) + { + UNUSED_VARIABLE(uint_to_str(m_instances[index].init_params.next_retr, next_retr_str, NEXT_RETR_MAX_LENGTH)); + op_length += sizeof(OPTION_TIMEOUT); // Time out option length. + op_length += strlen(next_retr_str) + 1; // The '\0' character ate the end of a string. + } + + if ((m_instances[index].init_params.block_size > 0) && + (m_instances[index].init_params.block_size != TFTP_DEFAULT_BLOCK_SIZE)) + { + UNUSED_VARIABLE(uint_to_str(m_instances[index].init_params.block_size, block_size_str, sizeof(block_size_str))); + op_length += sizeof(OPTION_BLKSIZE); // Time out option length. + op_length += strlen(block_size_str) + 1; // The '\0' character ate the end of a string. + } + + op_length += sizeof(OPTION_SIZE); // TFTP tsize option length. + + if (type == TYPE_RRQ) + { + op_length += sizeof(OPTION_SIZE_REQUEST_VALUE); // Just send 0 to inform other side that you support this option and would like to receive file size inside OptionACK. + } + + if (type == TYPE_WRQ) + { + UNUSED_VARIABLE(uint_to_str(m_instances[index].p_file->file_size, file_size_str, sizeof(file_size_str))); + op_length += strlen(file_size_str) + 1; + } + + if (m_instances[index].p_password != NULL) + { + if (m_instances[index].p_password[0] != '\0') + { + op_length += strlen(m_instances[index].p_password) + 1; // Password is always sent as last string without option name. + } + } + + return op_length; +} + +/**@brief This function inserts options into request packet payload using passed option iterator. + * + * @param[in] index Index of TFTP instance. + * @param[in] type Type of TFTP request. + * @param[in] p_iter Iterator which will be used to set options. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t insert_options(uint32_t index, uint32_t type, option_iter_t * p_iter) +{ + uint32_t err_code = NRF_SUCCESS; + char next_retr_str[NEXT_RETR_MAX_LENGTH]; + char block_size_str[BLKSIZE_MAX_LENGTH]; + char file_size_str[FILE_SIZE_MAX_LENGTH]; + + if (type == TYPE_RRQ) + { + err_code = op_set(p_iter, OPTION_SIZE, OPTION_SIZE_REQUEST_VALUE); + } + + if (type == TYPE_WRQ) + { + UNUSED_VARIABLE(uint_to_str(m_instances[index].p_file->file_size, file_size_str, FILE_SIZE_MAX_LENGTH)); + err_code = op_set(p_iter, OPTION_SIZE, file_size_str); + } + + if (err_code == NRF_SUCCESS) + { + if ((m_instances[index].init_params.next_retr > 0) && + (m_instances[index].init_params.next_retr < 256)) + { + UNUSED_VARIABLE(uint_to_str(m_instances[index].init_params.next_retr, next_retr_str, NEXT_RETR_MAX_LENGTH)); + err_code = op_set(p_iter, OPTION_TIMEOUT, next_retr_str); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + if ((m_instances[index].init_params.block_size > 0) && + (m_instances[index].init_params.block_size != TFTP_DEFAULT_BLOCK_SIZE)) + { + UNUSED_VARIABLE(uint_to_str(m_instances[index].init_params.block_size, block_size_str, BLKSIZE_MAX_LENGTH)); + err_code = op_set(p_iter, OPTION_BLKSIZE, block_size_str); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + if (m_instances[index].p_password != NULL) + { + if (m_instances[index].p_password[0] != '\0') + { + err_code = op_set(p_iter, NULL, m_instances[index].p_password); + } + } + } + + return err_code; +} + +/**@Sends Read/Write TFTP Request. + * + * @param[in] type Type of request (allowed values: TYPE_RRQ and TYPE_WRQ). + * @param[in] p_tftp Pointer to the TFTP instance (from user space). + * @param[in] p_file Pointer to the file, which should be assigned to passed instance. + * @param[in] p_params Pointer to transmission parameters structure (retransmission time, block size). + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +static uint32_t send_request(uint32_t type, + iot_tftp_t * p_tftp, + iot_file_t * p_file, + const char * p_path) +{ + uint32_t err_code; + iot_pbuffer_t * p_buffer; + iot_pbuffer_alloc_param_t buffer_param; + uint8_t * p_rrq_packet; + option_iter_t rrq_iter; + uint32_t index; + uint32_t byte_index; + + err_code = find_instance(p_tftp, &index); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + if (p_path == NULL || p_path[0] == '\0') + { + TFTP_ERR("[TFTP]: Invalid path passed.\r\n"); + return (NRF_ERROR_INVALID_PARAM | IOT_TFTP_ERR_BASE); + } + + if (p_file != NULL && p_file->p_filename[0] == '\0') + { + TFTP_ERR("Invalid file name passed!"); + return (NRF_ERROR_INVALID_PARAM | IOT_TFTP_ERR_BASE); + } + + if (m_instances[index].state != STATE_IDLE) + { + TFTP_ERR("Invalid instance state!"); + return (NRF_ERROR_INVALID_STATE | IOT_TFTP_ERR_BASE); + } + + // Assign file with TFTP instance. + m_instances[index].p_file = p_file; + m_instances[index].p_path = p_path; + m_instances[index].block_id = 0; + m_instances[index].dst_tid = m_instances[index].dst_port; + + memset(&buffer_param, 0, sizeof(buffer_param)); + buffer_param.type = UDP6_PACKET_TYPE; + buffer_param.flags = PBUFFER_FLAG_DEFAULT; + + // Calculate size of required packet. + buffer_param.length = TFTP_HEADER_SIZE; // Bytes reserved for TFTP opcode value. + buffer_param.length += strlen(p_path) + 1; // File name with '\0' character. + buffer_param.length += sizeof(OPTION_MODE_OCTET); // Mode option value length. + + TFTP_TRC("Estimated packet length without options: %ld.", buffer_param.length); + + buffer_param.length += count_options_length(index, type); // TFTP options. + + TFTP_TRC("Estimated packet length with options: %ld.", buffer_param.length); + + // Allocate packet buffer. + err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + memset(p_buffer->p_payload, 0, buffer_param.length); + byte_index = 0; + + // Compose TFTP Read Request according to configuration. + p_rrq_packet = p_buffer->p_payload; + uint16_t size = uint16_encode(HTONS(type), &p_rrq_packet[byte_index]); + byte_index += size; + + // Initialization of option iterator. + op_init(&rrq_iter, (char *)&p_rrq_packet[byte_index], buffer_param.length - TFTP_HEADER_SIZE); // Options uses whole packet except opcode. + rrq_iter.p_start[0] = '\0'; + + // Insert file path and mode strings. + err_code = op_set(&rrq_iter, NULL, p_path); + PBUFFER_FREE_IF_ERROR(err_code); + + err_code = op_set(&rrq_iter, NULL, OPTION_MODE_OCTET); + PBUFFER_FREE_IF_ERROR(err_code); + + // Insert TFTP options into packet. + err_code = insert_options(index, type, &rrq_iter); + PBUFFER_FREE_IF_ERROR(err_code); + + // Change instance status to connecting. + if (type == TYPE_RRQ) + { + m_instances[index].state = STATE_CONNECTING_RRQ; + } + else + { + m_instances[index].state = STATE_CONNECTING_WRQ; + } + + // Send read request. + UNUSED_VARIABLE(retr_timer_reset(index)); + err_code = udp6_socket_sendto(&m_instances[index].socket, + &m_instances[index].addr, + m_instances[index].dst_tid, + p_buffer); + + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Unable to send request!"); + m_instances[index].state = STATE_IDLE; + } + + PBUFFER_FREE_IF_ERROR(err_code); + + return NRF_SUCCESS; +} + +/**@brief Function used in order to change initial connection parameters. */ +uint32_t iot_tftp_set_params(iot_tftp_t * p_tftp, iot_tftp_trans_params_t * p_params) +{ + uint32_t err_code; + uint32_t index; + + NULL_PARAM_CHECK(p_params); + NULL_PARAM_CHECK(p_tftp); + + TFTP_ENTRY(); + + TFTP_MUTEX_LOCK(); + + err_code = find_instance(p_tftp, &index); + if (err_code == NRF_SUCCESS) + { + // Modifying connection parameters could be done only when TFTP instance is disconnected. + // NOTE: STATE_FREE is not allowed, because there have to be a moment (e.g. after calling iot_tftp_init()) when transmission parameters were set to default values. + if (m_instances[index].state == STATE_IDLE) + { + // Reset connection parameters to initial values. They will be set (negotiated) after get/put call. + memcpy(&m_instances[index].init_params, p_params, sizeof(iot_tftp_trans_params_t)); + } + else + { + err_code = (NRF_ERROR_INVALID_STATE | IOT_TFTP_ERR_BASE); + + TFTP_ERR("Cannot modify connection parameters inside %d state!", m_instances[index].state); + } + } + else + { + TFTP_ERR("Unable to find TFTP instance!"); + } + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return err_code; +} + +/**@brief Function for performing retransmissions of TFTP acknowledgments. */ +void iot_tftp_timeout_process(iot_timer_time_in_ms_t wall_clock_value) +{ + uint32_t index; + uint32_t err_code; + + UNUSED_PARAMETER(wall_clock_value); + + TFTP_MUTEX_LOCK(); + + for (index = 0; index < TFTP_MAX_INSTANCES; index++) + { + if ((m_instances[index].state == STATE_CONNECTING_RRQ) || + (m_instances[index].state == STATE_CONNECTING_WRQ) || + (m_instances[index].state == STATE_SENDING) || + (m_instances[index].state == STATE_RECEIVING)) + { + TFTP_ENTRY(); + TFTP_TRC("Current timer: %ld, %ld", m_instances[index].request_timeout, wall_clock_value); + + if (instance_timer_is_expired(index)) + { + err_code = NRF_SUCCESS; + + if (m_instances[index].retries < TFTP_MAX_RETRANSMISSION_COUNT) + { + TFTP_TRC("Query retransmission [%d] for file %s.", + m_instances[index].retries, m_instances[index].p_file->p_filename); + + // Increase retransmission number. + m_instances[index].retries++; + + TFTP_TRC("Compose packet for retransmission."); + // Send packet again. + if (m_instances[index].state == STATE_RECEIVING) + { + TFTP_TRC("Retransmission of ACK packet."); + err_code = create_ack_packet(index, m_instances[index].block_id); + + if (err_code == NRF_SUCCESS) + { + err_code = send_response(&index); + } + else + { + TFTP_ERR("Failed to create packet!"); + handle_evt_err(index, err_code, NULL); + } + } + else if (m_instances[index].state == STATE_SENDING) + { + TFTP_TRC("Retransmission of DATA packet."); + err_code = create_data_packet(index, m_instances[index].block_id - 1); + + if (err_code == NRF_SUCCESS) + { + if (m_instances[index].p_file->p_callback == NULL) + { + err_code = send_response(&index); + } + } + else + { + TFTP_ERR("Failed to create packet!"); + handle_evt_err(index, err_code, NULL); + } + } + else if (m_instances[index].state == STATE_CONNECTING_RRQ) + { + TFTP_TRC("OACK time out. Retransmit RRQ."); + m_instances[index].state = STATE_IDLE; + err_code = send_request(TYPE_RRQ, &index, m_instances[index].p_file, m_instances[index].p_path); + } + else if (m_instances[index].state == STATE_CONNECTING_WRQ) + { + TFTP_TRC("OACK time out. Retransmit WRQ."); + m_instances[index].state = STATE_IDLE; + err_code = send_request(TYPE_WRQ, &index, m_instances[index].p_file, NULL); + } + else + { + TFTP_TRC("In idle state."); + } + } + else + { + TFTP_ERR("TFTP server did not response on query for file %s.", + m_instances[index].p_file->p_filename); + + // No response from server. + err_code = TFTP_REMOTE_UNREACHABLE; + } + + if (err_code != NRF_SUCCESS) + { + // Inform application that timeout occurs. + TFTP_ERR("Timeout error."); + handle_evt_err(index, err_code, NULL); + } + } + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return; + } + } + + TFTP_MUTEX_UNLOCK(); +} + +/**@brief Initializes TFTP client. */ +uint32_t iot_tftp_init(iot_tftp_t * p_tftp, iot_tftp_init_t * p_init_params) +{ + uint32_t index = 0; + uint32_t err_code; + + NULL_PARAM_CHECK(p_tftp); + NULL_PARAM_CHECK(p_init_params); + NULL_PARAM_CHECK(p_init_params->p_ipv6_addr); + + TFTP_ENTRY(); + + TFTP_MUTEX_LOCK(); + + // Find first available instance. + err_code = find_free_instance(&index); + if (err_code == NRF_SUCCESS) + { + // Reset instance values. + instance_reset(index); + + // Assign new values. + *p_tftp = index; + m_instances[index].callback = p_init_params->callback; + m_instances[index].src_tid = p_init_params->src_port; + m_instances[index].dst_port = p_init_params->dst_port; + m_instances[index].p_password = p_init_params->p_password; + m_instances[index].dst_tid = m_instances[index].dst_port; + memcpy(&m_instances[index].addr, p_init_params->p_ipv6_addr, sizeof(ipv6_addr_t)); + + // Configure socket. + err_code = udp6_socket_allocate(&m_instances[index].socket); + if (err_code == NRF_SUCCESS) + { + err_code = udp6_socket_bind(&m_instances[index].socket, + IPV6_ADDR_ANY, + p_init_params->src_port); + if (err_code == NRF_SUCCESS) + { + // Attach callback. + err_code = udp6_socket_recv(&m_instances[index].socket, client_process); + if (err_code == NRF_SUCCESS) + { + m_instances[index].state = STATE_IDLE; + } + } + + if (err_code != NRF_SUCCESS) + { + (void)udp6_socket_free(&m_instances[index].socket); + + TFTP_ERR("UDP socket configuration failure!"); + } + } + } + else + { + TFTP_ERR("No more free instances left!"); + } + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return err_code; +} + +/**@brief Resets TFTP client instance, so it is possible to make another request after error. */ +uint32_t iot_tftp_abort(iot_tftp_t * p_tftp) +{ + uint32_t err_code; + uint32_t index; + + NULL_PARAM_CHECK(p_tftp); + + TFTP_ENTRY(); + + TFTP_MUTEX_LOCK(); + + err_code = find_instance(p_tftp, &index); + if (err_code == NRF_SUCCESS) + { + instance_abort(index); + } + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return err_code; +} + +/**@brief Frees assigned sockets. */ +uint32_t iot_tftp_uninit(iot_tftp_t * p_tftp) +{ + uint32_t err_code; + uint32_t index; + + NULL_PARAM_CHECK(p_tftp); + + TFTP_ENTRY(); + + TFTP_MUTEX_LOCK(); + + err_code = find_instance(p_tftp, &index); + if (err_code == NRF_SUCCESS) + { + if (m_instances[index].state == STATE_SEND_HOLD || + m_instances[index].state == STATE_RECV_HOLD || + m_instances[index].state == STATE_SENDING || + m_instances[index].state == STATE_RECEIVING) + { + // Free pbuffer. + err_code = iot_pbuffer_free(m_instances[index].p_packet, true); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Cannot free pbuffer - %p", m_instances[index].p_packet); + } + + err_code = iot_file_fclose(m_instances[index].p_file); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Cannot close file - %p", m_instances[index].p_file); + } + } + + if (m_instances[index].state != STATE_IDLE) + { + handle_evt_err(index, TFTP_UNDEFINED_ERROR, UNINT_ERROR_MSG); + } + + (void)udp6_socket_free(&m_instances[index].socket); + m_instances[index].state = STATE_FREE; + } + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return err_code; +} + +/**@brief Retrieves file from remote server into p_file. */ +uint32_t iot_tftp_get(iot_tftp_t * p_tftp, iot_file_t * p_file, const char * p_path) +{ + uint32_t err_code; + + NULL_PARAM_CHECK(p_tftp); + NULL_PARAM_CHECK(p_path); + if (p_file != NULL) + { + NULL_PARAM_CHECK(p_file->p_filename); + } + + TFTP_ENTRY(); + + TFTP_MUTEX_LOCK(); + + err_code = send_request(TYPE_RRQ, p_tftp, p_file, p_path); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Error while sending read request. Reason: %08lx.", err_code); + } + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return err_code; +} + +/**@brief Sends local file p_file to a remote server. */ +uint32_t iot_tftp_put(iot_tftp_t * p_tftp, iot_file_t * p_file, const char * p_path) +{ + uint32_t err_code; + + NULL_PARAM_CHECK(p_tftp); + NULL_PARAM_CHECK(p_file); + NULL_PARAM_CHECK(p_file->p_filename); + + TFTP_ENTRY(); + + TFTP_MUTEX_LOCK(); + + err_code = send_request(TYPE_WRQ, p_tftp, p_file, p_path); + if (err_code != NRF_SUCCESS) + { + TFTP_ERR("Error while sending write request. Reason: %08lx.", err_code); + } + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return err_code; +} + +/**@brief Holds transmission of ACK (use in order to slow transmission). */ +uint32_t iot_tftp_hold(iot_tftp_t * p_tftp) +{ + uint32_t index; + uint32_t err_code; + + NULL_PARAM_CHECK(p_tftp); + + TFTP_ENTRY(); + + TFTP_MUTEX_LOCK(); + + err_code = find_instance(p_tftp, &index); + if (err_code == NRF_SUCCESS) + { + // Hold transfer. + err_code = transfer_hold(index); + } + else + { + TFTP_ERR("Hold called on unknown TFTP instance."); + } + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return err_code; +} + +/**@brief Resumes transmission. */ +uint32_t iot_tftp_resume(iot_tftp_t * p_tftp) +{ + uint32_t index; + uint32_t err_code; + + NULL_PARAM_CHECK(p_tftp); + + TFTP_ENTRY(); + + TFTP_MUTEX_LOCK(); + + err_code = find_instance(p_tftp, &index); + if (err_code == NRF_SUCCESS) + { + err_code = transfer_resume(index); + } + else + { + TFTP_ERR("Failed to find instance."); + } + + TFTP_MUTEX_UNLOCK(); + + TFTP_EXIT(); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.h new file mode 100644 index 0000000..0405af2 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/tftp/iot_tftp.h @@ -0,0 +1,245 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file iot_tftp.h + * + * @defgroup iot_tftp TFTP Application Interface for Nordic's IPv6 stack + * @ingroup iot_sdk_stack + * @{ + * @brief Trivial File Transfer Protocol module provides implementation of TFTP Client. + * + */ + +#ifndef IOT_TFTP_H__ +#define IOT_TFTP_H__ + +#include "sdk_common.h" +#include "iot_common.h" +#include "iot_timer.h" +#include "iot_file.h" +#include "iot_defines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief TFTP global instance number. */ +typedef uint32_t iot_tftp_t; + +/**@brief TFTP module Events. */ +typedef enum +{ + IOT_TFTP_EVT_ERROR, /**< Event code indicating that an error occurred. */ + IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED, /**< Event code indicating that a data packet was received during read transfer. */ + IOT_TFTP_EVT_TRANSFER_GET_COMPLETE, /**< Event code indicating that transfer read was completed. */ + IOT_TFTP_EVT_TRANSFER_PUT_COMPLETE, /**< Event code indicating that transfer write was completed. */ +} iot_tftp_evt_id_t; + +/**@brief TFTP error event structure. */ +typedef struct +{ + uint32_t code; /**< Error code. */ + char * p_msg; /**< Message describing the reason or NULL if no description is available. */ + uint32_t size_transfered; /**< In case of an error, variable indicates a number of successfully read or write bytes. */ +} iot_tftp_evt_err_t; + +/**@brief TFTP data received event structure. */ +typedef struct +{ + uint8_t * p_data; /**< Pointer to received data chunk. */ + uint16_t size; /**< Size of received data chunk. */ +} iot_tftp_evt_data_received_t; + +/**@brief TFTP event structure. */ +typedef union +{ + iot_tftp_evt_err_t err; /**< Error event structure. Used only in case of IOT_TFTP_EVT_ERROR error. */ + iot_tftp_evt_data_received_t data_received; /**< Data received event structure. Used only in case of IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED event. */ +} iot_tftp_evt_param_t; + +/**@brief Asynchronous event type. */ +typedef struct +{ + iot_tftp_evt_id_t id; /**< Event code. */ + iot_tftp_evt_param_t param; /**< Union to structures describing event. */ + iot_file_t * p_file; /**< File associated with TFTP transfer. */ +} iot_tftp_evt_t; + +/**@brief TFTP Transmission initialization structure (both GET and PUT). */ +typedef struct +{ + uint32_t next_retr; /**< Number of seconds between retransmissions. */ + uint16_t block_size; /**< Maximum or negotiated size of data block. */ +} iot_tftp_trans_params_t; + +/**@brief User callback from TFTP module. + * + * @note TFTP module user callback will be invoked even if user asks TFTP to abort (TFTP error event). + * + * @param[in] p_tftp Pointer to the TFTP instance. + * @param[in] p_evt Pointer to the TFTP event structure, describing reason. + * + * @retval None. + */ +typedef void (*iot_tftp_callback_t)(iot_tftp_t * p_tftp, iot_tftp_evt_t * p_evt); + + +/**@brief TFTP initialization structure. */ +typedef struct +{ + ipv6_addr_t * p_ipv6_addr; /**< IPv6 address of the server. */ + uint16_t src_port; /**< Source port (local UDP port) from which all request and data will be sent. Should be choosen randomly. */ + uint16_t dst_port; /**< Destination port - UDP port on which server listens for new connections. */ + iot_tftp_callback_t callback; /**< Reference to the user callback. */ + const char * p_password; /**< Server password for all requests. Shall be NULL if no password is required. */ +} iot_tftp_init_t; + + +/**@brief Initializes TFTP client. + * + * @param[in] p_tftp Pointer to the TFTP instance. Should not be NULL. + * @param[in] p_init_params Initialization structure for TFTP client. Should not be NULL. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_tftp_init(iot_tftp_t * p_tftp, iot_tftp_init_t * p_init_params); + + +/**@brief Function used in order to change initial connection parameters. + * + * @param[in] p_tftp Reference to the TFTP instance. + * @param[in] p_params Pointer to transmission parameters structure. Should not be NULL. + * + * @retval NRF_SUCCESS if parameters successfully set, else an error code indicating reason + * for failure. + */ +uint32_t iot_tftp_set_params(iot_tftp_t * p_tftp, iot_tftp_trans_params_t * p_params); + + +/**@brief Retrieves file from remote server into p_file. + * + * If @p p_file is a NULL pointer, the content of received file can be retrieved by handling + * @ref IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED event. This event is generated each time a data + * packet (containing a chunk of requested file) is received. + * IOT_TFTP_EVT_TRANSFER_GET_COMPLETE event is generated after download is complete. + * + * @note This function should not be called until previous download operation is completed. + * + * @param[in] p_tftp Pointer to the TFTP instance. + * @param[in] p_file Reference to the file from which data should be read. + * @param[in] p_path Path of the requested file on the remote server. Shall not be NULL. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_tftp_get(iot_tftp_t * p_tftp, iot_file_t * p_file, const char * p_path); + + +/**@brief Sends local file p_file to a remote server. + * + * @param[in] p_tftp Pointer to the TFTP instance. + * @param[in] p_file Reference to the file to which data should be stored. Should not be NULL. + * @param[in] p_path Path of the file on the remote server. Shall not be NULL. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_tftp_put(iot_tftp_t * p_tftp, iot_file_t * p_file, const char * p_path); + + +/**@brief Holds transmission of ACK (use in order to slow transmission). + * + * @param[in] p_tftp Pointer to the TFTP instance. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_tftp_hold(iot_tftp_t * p_tftp); + + +/**@brief Resumes transmission. + * + * @param[in] p_tftp Pointer to the TFTP instance. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_tftp_resume(iot_tftp_t * p_tftp); + + +/**@brief Resets TFTP client instance, so it is possible to make another request after error. + * + * @param[in] p_tftp Pointer to the TFTP instance. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_tftp_abort(iot_tftp_t * p_tftp); + + +/**@brief Frees assigned sockets. + * + * @param[in] p_tftp Pointer to the TFTP instance. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t iot_tftp_uninit(iot_tftp_t * p_tftp); + + +/**@brief Function for performing retransmissions of TFTP acknowledgments. + * + * @note TFTP module implements the retransmission mechanism by invoking this function periodically. + * So that method has to be added to IoT Timer client list and has to be called with minimum of + * TFTP_RETRANSMISSION_INTERVAL resolution. + * + * @param[in] wall_clock_value The value of the wall clock that triggered the callback. + * + * @retval None. + */ +void iot_tftp_timeout_process(iot_timer_time_in_ms_t wall_clock_value); + +#ifdef __cplusplus +} +#endif + +#endif // IOT_TFTP_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp.h new file mode 100644 index 0000000..ae316b0 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp.h @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @cond */ + /** @file udp.h + * + * @defgroup iot_udp UDP Module Header. + * @ingroup iot_sdk + * @{ + * @brief User Datagram Protocol module header defining interface between + * UDP and other IP stack layers which are not exposed to the application. + */ + +#ifndef UDP_H__ +#define UDP_H__ + +#include "sdk_config.h" +#include "sdk_common.h" +#include "ipv6_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Module initialization function called from ipv6_init(). This shall not be called by the + * application explicitly. + * + * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason + * for failure. + */ +uint32_t udp_init(void); + + +/** + * @brief Function to feed incoming UDP packets to the module. To be called by the IPv6 + * stack only and never by the application. + * + * @param[in] p_interface Identifies network interface on which the packet is received. + * @param[in] p_ip_header IP Header of the UDP packet being fed to the module. + * @param[in] p_packet UDP packet being notified to the module. p_packet->p_payload points the + * IPv6 payload and p_packet->length indicates total length of the payload. + * + * @note This routine is called by the stack with next header field value is set to UDP protocol + * value of 17. + * + * @retval NRF_SUCCESS on successful handling of the packet, else an error code indicating reason + * for failure. + */ +uint32_t udp_input(const iot_interface_t * p_interface, + const ipv6_header_t * p_ip_header, + iot_pbuffer_t * p_packet); + +#ifdef __cplusplus +} +#endif + +#endif //UDP_H__ + +/**@} */ + +/** @endcond */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp6.c new file mode 100644 index 0000000..0572c4a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/udp/udp6.c @@ -0,0 +1,708 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "nordic_common.h" +#include "sdk_common.h" +#include "sdk_config.h" +#include "iot_common.h" +#include "iot_pbuffer.h" +#include "udp_api.h" +#include "udp.h" +#include "ipv6_utils.h" + +#if UDP6_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME udp6 + +#define NRF_LOG_LEVEL UDP6_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR UDP6_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR UDP6_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define UDP6_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define UDP6_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define UDP6_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define UDP6_ENTRY() UDP6_TRC(">> %s", __func__) +#define UDP6_EXIT() UDP6_TRC("<< %s", __func__) + +#else // UDP6_CONFIG_LOG_ENABLED + +#define UDP6_TRC(...) /**< Disables traces. */ +#define UDP6_DUMP(...) /**< Disables dumping of octet streams. */ +#define UDP6_ERR(...) /**< Disables error logs. */ + +#define UDP6_ENTRY(...) +#define UDP6_EXIT(...) + +#endif // UDP6_CONFIG_LOG_ENABLED + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * UDP_DISABLE_API_PARAM_CHECK should be set to 1 to disable these checks. + * + * @{ + */ +#if (UDP6_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Macro to check is module is initialized before requesting one of the module procedures. */ +#define VERIFY_MODULE_IS_INITIALIZED() \ + if (m_initialization_state == false) \ + { \ + return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_UDP6_ERR_BASE); \ + } + +/**@brief Macro to check is module is initialized before requesting one of the module + procedures but does not use any return code. */ +#define VERIFY_MODULE_IS_INITIALIZED_VOID() \ + if (m_initialization_state == false) \ + { \ + return; \ + } + +/** + * @brief Verify NULL parameters are not passed to API by application. + */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_UDP6_ERR_BASE); \ + } + +/** + * @brief Verify socket id passed on the API by application is valid. + */ +#define VERIFY_SOCKET_ID(ID) \ + if (((ID) >= UDP6_MAX_SOCKET_COUNT)) \ + { \ + return (NRF_ERROR_INVALID_ADDR | IOT_UDP6_ERR_BASE); \ + } + +/** + * @brief Verify socket id passed on the API by application is valid. + */ +#define VERIFY_PORT_NUMBER(PORT) \ + if ((PORT) == 0) \ + { \ + return (NRF_ERROR_INVALID_PARAM | IOT_UDP6_ERR_BASE); \ + } + +/** + * @brief Verify socket id passed on the API by application is valid. + */ +#define VERIFY_NON_ZERO_LENGTH(LEN) \ + if ((LEN) == 0) \ + { \ + return (NRF_ERROR_INVALID_LENGTH | IOT_UDP6_ERR_BASE); \ + } + +#else // UDP6_DISABLE_API_PARAM_CHECK + +#define VERIFY_MODULE_IS_INITIALIZED() +#define VERIFY_MODULE_IS_INITIALIZED_VOID() +#define NULL_PARAM_CHECK(PARAM) +#define VERIFY_SOCKET_ID(ID) +#endif //UDP6_DISABLE_API_PARAM_CHECK + +/** + * @defgroup ble_ipsp_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case need arises to use an alternative architecture. + * @{ + */ +#define UDP_MUTEX_LOCK() SDK_MUTEX_LOCK(m_udp_mutex) /**< Lock module using mutex */ +#define UDP_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_udp_mutex) /**< Unlock module using mutex */ +/** @} */ + +#define UDP_PORT_FREE 0 /**< Reserved port of the socket, indicates that port is free. */ + +/**@brief UDP Socket Data needed by the module to manage it. */ +typedef struct +{ + uint16_t local_port; /**< Local Port of the socket. */ + uint16_t remote_port; /**< Remote port of the socket. */ + ipv6_addr_t local_addr; /**< Local IPv6 Address of the socket. */ + ipv6_addr_t remote_addr; /**< Remote IPv6 Address of the socket. */ + udp6_handler_t rx_cb; /**< Callback registered by application to receive data on the socket. */ + void * p_app_data; /**< Application data mapped to the socket using the udp6_app_data_set. */ +} udp_socket_entry_t; + + +SDK_MUTEX_DEFINE(m_udp_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ +static bool m_initialization_state = false; /**< Variable to maintain module initialization state. */ +static udp_socket_entry_t m_socket[UDP6_MAX_SOCKET_COUNT]; /**< Table of sockets managed by the module. */ + + +/** @brief Initializes socket managed by the module. */ +static void udp_socket_init(udp_socket_entry_t * p_socket) +{ + p_socket->local_port = UDP_PORT_FREE; + p_socket->remote_port = UDP_PORT_FREE; + p_socket->rx_cb = NULL; + p_socket->p_app_data = NULL; + IPV6_ADDRESS_INITIALIZE(&p_socket->local_addr); + IPV6_ADDRESS_INITIALIZE(&p_socket->remote_addr); +} + +/** + * @brief Find UDP socket based on local port. If found its index to m_socket table is returned. + * else UDP6_MAX_SOCKET_COUNT is returned. + */ +static uint32_t socket_find(uint16_t port) +{ + uint32_t index; + + for (index = 0; index < UDP6_MAX_SOCKET_COUNT; index++) + { + if (m_socket[index].local_port == port) + { + break; + } + } + + return index; +} + + +uint32_t udp_init(void) +{ + uint32_t index; + + UDP6_ENTRY(); + + SDK_MUTEX_INIT(m_udp_mutex); + + UDP_MUTEX_LOCK(); + + for (index = 0; index < UDP6_MAX_SOCKET_COUNT; index++) + { + udp_socket_init(&m_socket[index]); + } + + m_initialization_state = true; + + UDP6_EXIT(); + + UDP_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t udp6_socket_allocate(udp6_socket_t * p_socket) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_socket); + + UDP6_ENTRY(); + + UDP_MUTEX_LOCK(); + + //Search for an unassigned socket. + const uint32_t socket_id = socket_find(UDP_PORT_FREE); + uint32_t err_code = NRF_SUCCESS; + + if (socket_id != UDP6_MAX_SOCKET_COUNT) + { + UDP6_TRC("Assigned socket 0x%08lX", socket_id); + + // Found a free socket. Assign. + p_socket->socket_id = socket_id; + } + else + { + // No free socket found. + UDP6_ERR("No room for new socket."); + err_code = (NRF_ERROR_NO_MEM | IOT_UDP6_ERR_BASE); + } + + UDP_MUTEX_UNLOCK(); + + UDP6_EXIT(); + + return err_code; +} + + +uint32_t udp6_socket_free(const udp6_socket_t * p_socket) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_socket); + VERIFY_SOCKET_ID(p_socket->socket_id); + + UDP6_ENTRY(); + + UDP_MUTEX_LOCK(); + + udp_socket_init(&m_socket[p_socket->socket_id]); + + UDP_MUTEX_UNLOCK(); + + UDP6_EXIT(); + + return NRF_SUCCESS; +} + + +uint32_t udp6_socket_recv(const udp6_socket_t * p_socket, + const udp6_handler_t callback) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_socket); + NULL_PARAM_CHECK(callback); + VERIFY_SOCKET_ID(p_socket->socket_id); + VERIFY_PORT_NUMBER(m_socket[p_socket->socket_id].local_port); + + UDP6_ENTRY(); + + UDP_MUTEX_LOCK(); + + m_socket[p_socket->socket_id].rx_cb = callback; + + UDP_MUTEX_UNLOCK(); + + UDP6_EXIT(); + + return NRF_SUCCESS; +} + + +uint32_t udp6_socket_bind(const udp6_socket_t * p_socket, + const ipv6_addr_t * p_src_addr, + uint16_t src_port) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_socket); + NULL_PARAM_CHECK(p_src_addr); + VERIFY_SOCKET_ID(p_socket->socket_id); + VERIFY_PORT_NUMBER(src_port); + + UDP6_ENTRY(); + + UDP_MUTEX_LOCK(); + + uint32_t err_code = NRF_SUCCESS; + + // Change Host Byte Order to Network Byte Order. + src_port = HTONS(src_port); + + //Check if port is already registered. + for (uint32_t index = 0; index < UDP6_MAX_SOCKET_COUNT; index ++) + { + if (m_socket[index].local_port == src_port) + { + err_code = UDP_PORT_IN_USE; + } + } + + if (err_code == NRF_SUCCESS) + { + m_socket[p_socket->socket_id].local_port = src_port; + m_socket[p_socket->socket_id].local_addr = (*p_src_addr); + + } + UDP_MUTEX_UNLOCK(); + + UDP6_EXIT(); + + return err_code; +} + + +uint32_t udp6_socket_connect(const udp6_socket_t * p_socket, + const ipv6_addr_t * p_dest_addr, + uint16_t dest_port) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_socket); + NULL_PARAM_CHECK(p_dest_addr); + VERIFY_SOCKET_ID(p_socket->socket_id); + VERIFY_PORT_NUMBER(dest_port); + VERIFY_PORT_NUMBER(m_socket[p_socket->socket_id].local_port); + + UDP6_ENTRY(); + + UDP_MUTEX_LOCK(); + + m_socket[p_socket->socket_id].remote_port = HTONS(dest_port); + m_socket[p_socket->socket_id].remote_addr = (*p_dest_addr); + + UDP_MUTEX_UNLOCK(); + + UDP6_EXIT(); + + return NRF_SUCCESS; +} + + +uint32_t udp6_socket_send(const udp6_socket_t * p_socket, + iot_pbuffer_t * p_packet) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_socket); + NULL_PARAM_CHECK(p_packet); + NULL_PARAM_CHECK(p_packet->p_payload); + VERIFY_NON_ZERO_LENGTH(p_packet->length); + VERIFY_SOCKET_ID(p_socket->socket_id); + VERIFY_PORT_NUMBER(m_socket[p_socket->socket_id].local_port); + VERIFY_PORT_NUMBER(m_socket[p_socket->socket_id].remote_port); + + UDP6_ENTRY(); + + UDP_MUTEX_LOCK(); + + uint32_t err_code; + const udp_socket_entry_t * p_skt = &m_socket[p_socket->socket_id]; + const uint32_t header_size = UDP_HEADER_SIZE + IPV6_IP_HEADER_SIZE; + udp6_header_t * p_header = (udp6_header_t *)(p_packet->p_payload - UDP_HEADER_SIZE); + ipv6_header_t * p_ip_header = (ipv6_header_t *)(p_packet->p_payload - header_size); + iot_interface_t * p_interface = NULL; + uint16_t checksum; + + p_header->srcport = p_skt->local_port; + p_header->destport = p_skt->remote_port; + p_header->checksum = 0; + + p_header->length = HTONS(p_packet->length + UDP_HEADER_SIZE); + + // Pack destination address. + p_ip_header->destaddr = p_skt->remote_addr; + + // Pack source address. + if ((0 == IPV6_ADDRESS_CMP(&p_skt->local_addr, IPV6_ADDR_ANY))) + { + err_code = ipv6_address_find_best_match(&p_interface, + &p_ip_header->srcaddr, + &p_ip_header->destaddr); + } + else + { + err_code = ipv6_address_find_best_match(&p_interface, + NULL, + &p_ip_header->destaddr); + + p_ip_header->srcaddr = p_skt->local_addr; + } + + if (err_code == NRF_SUCCESS) + { + // Pack next header. + p_ip_header->next_header = IPV6_NEXT_HEADER_UDP; + + //Pack HOP Limit. + p_ip_header->hoplimit = IPV6_DEFAULT_HOP_LIMIT; + + //Traffic class and flow label. + p_ip_header->version_traffic_class = 0x60; + p_ip_header->traffic_class_flowlabel = 0x00; + p_ip_header->flowlabel = 0x0000; + + // Length. + p_ip_header->length = HTONS(p_packet->length + UDP_HEADER_SIZE); + + checksum = p_packet->length + UDP_HEADER_SIZE + IPV6_NEXT_HEADER_UDP; + + ipv6_checksum_calculate(p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_packet->p_payload - UDP_HEADER_SIZE, + p_packet->length + UDP_HEADER_SIZE, + &checksum, + true); + + p_header->checksum = HTONS((~checksum)); + + p_packet->p_payload -= header_size; + p_packet->length += header_size; + + err_code = ipv6_send(p_interface, p_packet); + } + else + { + err_code = UDP_INTERFACE_NOT_READY; + } + + UDP_MUTEX_UNLOCK(); + + UDP6_EXIT(); + + return err_code; +} + + +uint32_t udp6_socket_sendto(const udp6_socket_t * p_socket, + const ipv6_addr_t * p_dest_addr, + uint16_t dest_port, + iot_pbuffer_t * p_packet) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_socket); + NULL_PARAM_CHECK(p_dest_addr); + NULL_PARAM_CHECK(p_packet); + NULL_PARAM_CHECK(p_packet->p_payload); + VERIFY_NON_ZERO_LENGTH(p_packet->length); + VERIFY_SOCKET_ID(p_socket->socket_id); + VERIFY_PORT_NUMBER(dest_port); + + UDP6_ENTRY(); + + UDP_MUTEX_LOCK(); + + uint32_t err_code; + const udp_socket_entry_t * p_skt = &m_socket[p_socket->socket_id]; + const uint32_t header_size = UDP_HEADER_SIZE + IPV6_IP_HEADER_SIZE; + udp6_header_t * p_header = (udp6_header_t *)(p_packet->p_payload - UDP_HEADER_SIZE); + ipv6_header_t * p_ip_header = (ipv6_header_t *)(p_packet->p_payload - header_size); + iot_interface_t * p_interface = NULL; + uint16_t checksum; + + p_header->srcport = p_skt->local_port; + p_header->destport = HTONS(dest_port); + p_header->checksum = 0; + + checksum = p_packet->length + UDP_HEADER_SIZE + IPV6_NEXT_HEADER_UDP; + + p_header->length = HTONS(p_packet->length + UDP_HEADER_SIZE); + + //Pack destination address. + p_ip_header->destaddr = *p_dest_addr; + + // Pack source address. + if ((0 == IPV6_ADDRESS_CMP(&p_skt->local_addr, IPV6_ADDR_ANY))) + { + err_code = ipv6_address_find_best_match(&p_interface, + &p_ip_header->srcaddr, + &p_ip_header->destaddr); + } + else + { + err_code = ipv6_address_find_best_match(&p_interface, + NULL, + &p_ip_header->destaddr); + + p_ip_header->srcaddr = p_skt->local_addr; + } + + if (err_code == NRF_SUCCESS) + { + //Pack next header. + p_ip_header->next_header = IPV6_NEXT_HEADER_UDP; + + //Pack HOP Limit. + p_ip_header->hoplimit = IPV6_DEFAULT_HOP_LIMIT; + + //Traffic class and flow label. + p_ip_header->version_traffic_class = 0x60; + p_ip_header->traffic_class_flowlabel = 0x00; + p_ip_header->flowlabel = 0x0000; + + // Length. + p_ip_header->length = HTONS(p_packet->length + UDP_HEADER_SIZE); + + ipv6_checksum_calculate(p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_packet->p_payload - UDP_HEADER_SIZE, + p_packet->length + UDP_HEADER_SIZE, + &checksum, + true); + + p_header->checksum = HTONS((~checksum)); + + p_packet->p_payload -= header_size; + p_packet->length += header_size; + + err_code = ipv6_send(p_interface, p_packet); + } + else + { + err_code = UDP_INTERFACE_NOT_READY; + } + + UDP_MUTEX_UNLOCK(); + + UDP6_EXIT(); + + return err_code; +} + + +uint32_t udp6_socket_app_data_set(const udp6_socket_t * p_socket) +{ + VERIFY_MODULE_IS_INITIALIZED(); + NULL_PARAM_CHECK(p_socket); + VERIFY_SOCKET_ID(p_socket->socket_id); + + //Note: no null check is performed on the p_app_data as it is permissible + //to pass on a NULL value if need be. + + UDP6_ENTRY(); + + UDP_MUTEX_LOCK(); + + m_socket[p_socket->socket_id].p_app_data = p_socket->p_app_data; + + UDP_MUTEX_UNLOCK(); + + UDP6_EXIT(); + + return NRF_SUCCESS; +} + + +uint32_t udp_input(const iot_interface_t * p_interface, + const ipv6_header_t * p_ip_header, + iot_pbuffer_t * p_packet) +{ + NULL_PARAM_CHECK(p_interface); + NULL_PARAM_CHECK(p_ip_header); + NULL_PARAM_CHECK(p_packet); + + UNUSED_VARIABLE(p_interface); + + uint32_t err_code = (NRF_ERROR_NOT_FOUND | IOT_UDP6_ERR_BASE); + + if ((p_packet->length > UDP_HEADER_SIZE) && (p_ip_header->length > UDP_HEADER_SIZE)) + { + UDP_MUTEX_LOCK(); + + UDP6_ENTRY(); + + uint32_t index; + udp6_header_t * p_udp_header = (udp6_header_t *)(p_packet->p_payload); + + // Check to which UDP socket, port and address was bind. + for (index = 0; index < UDP6_MAX_SOCKET_COUNT; index ++) + { + if (m_socket[index].local_port == p_udp_header->destport) + { + if ((0 == IPV6_ADDRESS_CMP(&m_socket[index].local_addr, IPV6_ADDR_ANY)) || + (0 == IPV6_ADDRESS_CMP(&m_socket[index].local_addr, &p_ip_header->destaddr))) + { + // Check if connection was established. + if (m_socket[index].remote_port == 0 || m_socket[index].remote_port == p_udp_header->srcport) + { + if ((0 == IPV6_ADDRESS_CMP(&m_socket[index].remote_addr, IPV6_ADDR_ANY)) || + (0 == IPV6_ADDRESS_CMP(&m_socket[index].remote_addr, &p_ip_header->srcaddr))) + { + err_code = NRF_SUCCESS; + break; + } + } + } + } + } + + if (index < UDP6_MAX_SOCKET_COUNT) + { + uint16_t checksum = p_packet->length + IPV6_NEXT_HEADER_UDP; + uint32_t process_result = NRF_SUCCESS; + uint16_t udp_hdr_length = NTOHS(p_udp_header->length); + + if (udp_hdr_length > p_packet->length) + { + UDP6_ERR("Received truncated packet, " + "payload length 0x%08lX, length in header 0x%08X.", + p_packet->length, NTOHS(p_udp_header->length)); + process_result = UDP_TRUNCATED_PACKET; + } + else if (udp_hdr_length < p_packet->length) + { + UDP6_ERR("Received malformed packet, " + "payload length 0x%08lX, length in header 0x%08X.", + p_packet->length, NTOHS(p_udp_header->length)); + + process_result = UDP_MALFORMED_PACKET; + } + else + { + ipv6_checksum_calculate(p_ip_header->srcaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_ip_header->destaddr.u8, IPV6_ADDR_SIZE, &checksum, false); + ipv6_checksum_calculate(p_packet->p_payload, p_packet->length, &checksum, false); + + if (checksum != 0 && checksum != 0xFFFF) + { + UDP6_ERR("Bad checksum detected."); + process_result = UDP_BAD_CHECKSUM; + } + } + + p_packet->p_payload = p_packet->p_payload + UDP_HEADER_SIZE; + p_packet->length -= UDP_HEADER_SIZE; + + //Found port for which data is intended. + const udp6_socket_t sock = {index, m_socket[index].p_app_data}; + + //Give application a callback if callback is registered. + if (m_socket[index].rx_cb != NULL) + { + UDP_MUTEX_UNLOCK(); + + // Change byte ordering given to application. + p_udp_header->destport = NTOHS(p_udp_header->destport); + p_udp_header->srcport = NTOHS(p_udp_header->srcport); + p_udp_header->length = NTOHS(p_udp_header->length); + p_udp_header->checksum = NTOHS(p_udp_header->checksum); + + err_code = m_socket[index].rx_cb(&sock, p_ip_header, p_udp_header, process_result, p_packet); + + UDP_MUTEX_LOCK(); + } + } + else + { + UDP6_ERR("Packet received on unknown port, dropping!"); + } + + UDP_MUTEX_UNLOCK(); + + UDP6_EXIT(); + } + else + { + UDP6_ERR("Packet of length less than UDP header size received!"); + err_code = (IOT_UDP6_ERR_BASE | NRF_ERROR_INVALID_LENGTH); + } + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.c new file mode 100644 index 0000000..8b70cba --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.c @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdint.h> +#include <stdbool.h> +#include "nordic_common.h" +#include "sdk_common.h" +#include "sdk_config.h" +#include "ipv6_utils.h" + +void ipv6_checksum_calculate(const uint8_t * p_data, uint16_t len, uint16_t * p_checksum, bool flip_flag) +{ + uint16_t checksum_even = (((*p_checksum) & 0xFF00) >> 8); + uint16_t checksum_odd = ((*p_checksum) & 0x00FF); + + while (len) + { + if ( len == 1 ) + { + checksum_even += (*p_data); + len -= 1; + } + else + { + checksum_even += *p_data++; + checksum_odd += *p_data++; + len -= 2; + } + + if (checksum_odd & 0xFF00) + { + checksum_even += ((checksum_odd & 0xFF00) >> 8); + checksum_odd = (checksum_odd & 0x00FF); + } + + if (checksum_even & 0xFF00) + { + checksum_odd += ((checksum_even & 0xFF00) >> 8); + checksum_even = (checksum_even & 0x00FF); + } + } + + checksum_even = (checksum_even << 8) + (checksum_odd & 0xFFFF); + + if (flip_flag) + { + // We use 0xFFFF instead of 0x0000 because of not operator. + if (checksum_even == 0xFFFF) + { + checksum_even = 0x0000; + } + } + + (*p_checksum) = (uint16_t)(checksum_even); +} + +void ipv6_header_init(ipv6_header_t * p_ip_header) +{ + p_ip_header->version_traffic_class = IPV6_DEFAULT_VER_TC; + p_ip_header->traffic_class_flowlabel = IPV6_DEFAULT_TC_FL; + p_ip_header->flowlabel = IPV6_DEFAULT_FL; + p_ip_header->next_header = IPV6_NEXT_HEADER_RESERVED; + p_ip_header->hoplimit = IPV6_DEFAULT_HOP_LIMIT; + p_ip_header->length = 0; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.h new file mode 100644 index 0000000..5cbdf15 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/utils/ipv6_utils.h @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file ipv6_utils.h + * + * @defgroup iot_utils Common utils + * @ingroup iot_sdk_common + * @{ + * @brief Abstracts common IOT macros needed by IOT modules. + * + * @details Common macros related to IOT are defined here for use by all IOT modules. + */ + +#ifndef IPV6_UTILS_H__ +#define IPV6_UTILS_H__ + +#include <stdint.h> +#include <stdbool.h> +#include "iot_defines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Function for calculating checksum using IPv6 algorithm. + * + * @param[in] p_data Pointer to the data needs to be checksummed. + * @param[in] len Length of the data. + * @param[in] p_checksum Pointer to actual value of checksum. + * @param[out] p_checksum Value of calculated checksum. + * + * @retval None. + */ +void ipv6_checksum_calculate(const uint8_t * p_data, + uint16_t len, + uint16_t * p_checksum, + bool flip_zero); + + +/**@brief Function for initializing default values of IPv6 Header. + * + * @note Function initializes Version, Traffic Class, Flow Label, Next Header, Hop Limit and + * Length fields. + * + * @param[in] p_ip_header Pointer to the IPv6 Header. + * + * @retval None. + */ +void ipv6_header_init(ipv6_header_t * p_ip_header); + +#ifdef __cplusplus +} +#endif + +#endif //IPV6_UTILS_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects.c new file mode 100644 index 0000000..638277a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects.c @@ -0,0 +1,553 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <string.h> +#include <stddef.h> + +#include "ipso_objects.h" +#include "lwm2m.h" +#include "lwm2m_tlv.h" + +//lint -e516 -save // Symbol '__INTADDR__()' has arg. type conflict +#define LWM2M_INSTANCE_OFFSET_SET(instance, type) \ + instance->proto.operations_offset = offsetof(type, operations); \ + instance->proto.resource_ids_offset = offsetof(type, resource_ids); +//lint -restore + +void ipso_instance_digital_input_init(ipso_digital_input_t * p_instance) +{ + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_digital_input_t); + + p_instance->proto.object_id = IPSO_SO_ID_DIGITAL_INPUT; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_digital_input_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[3] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[4] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[5] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[6] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[7] = LWM2M_OPERATION_CODE_READ; + + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_DIGITAL_INPUT_STATE; + p_instance->resource_ids[1] = IPSO_RR_ID_DIGITAL_INPUT_COUNTER; + p_instance->resource_ids[2] = IPSO_RR_ID_DIGITAL_INPUT_POLARITY; + p_instance->resource_ids[3] = IPSO_RR_ID_DIGITAL_INPUT_DEBOUNCE_PERIOD; + p_instance->resource_ids[4] = IPSO_RR_ID_DIGITAL_INPUT_EDGE_SELECTION; + p_instance->resource_ids[5] = IPSO_RR_ID_DIGITAL_INPUT_COUNTER_RESET; + p_instance->resource_ids[6] = IPSO_RR_ID_APPLICATION_TYPE; + p_instance->resource_ids[7] = IPSO_RR_ID_SENSOR_TYPE; + + +} + +void ipso_instance_digital_output_init(ipso_digital_output_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_digital_output_t); + + p_instance->proto.object_id = IPSO_SO_ID_DIGITAL_OUTPUT; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_digital_output_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[1] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[2] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_DIGITAL_OUTPUT_STATE; + p_instance->resource_ids[1] = IPSO_RR_ID_DIGITAL_OUTPUT_POLARITY; + p_instance->resource_ids[2] = IPSO_RR_ID_APPLICATION_TYPE; +} + + +void ipso_instance_analog_input_init(ipso_analog_input_t * p_instance) +{ + + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_analog_input_t); + + p_instance->proto.object_id = IPSO_SO_ID_ANALOGUE_INPUT; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_analog_input_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[6] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[7] = LWM2M_OPERATION_CODE_READ; + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_ANALOG_INPUT_CURRENT_VALUE; + p_instance->resource_ids[1] = IPSO_RR_ID_MIN_MEASURED_VALUE; + p_instance->resource_ids[2] = IPSO_RR_ID_MAX_MEASURED_VALUE; + p_instance->resource_ids[3] = IPSO_RR_ID_MIN_RANGE_VALUE; + p_instance->resource_ids[4] = IPSO_RR_ID_MAX_RANGE_VALUE; + p_instance->resource_ids[5] = IPSO_RR_ID_RESET_MIN_MAX_MEASURED_VALUES; + p_instance->resource_ids[6] = IPSO_RR_ID_APPLICATION_TYPE; + p_instance->resource_ids[7] = IPSO_RR_ID_SENSOR_TYPE; +} + +void ipso_instance_analog_output_init(ipso_analog_output_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_analog_output_t); + + p_instance->proto.object_id = IPSO_SO_ID_ANALOGUE_OUTPUT; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_analog_output_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_ANALOG_OUTPUT_CURRENT_VALUE; + p_instance->resource_ids[1] = IPSO_RR_ID_MIN_RANGE_VALUE; + p_instance->resource_ids[2] = IPSO_RR_ID_MAX_RANGE_VALUE; + p_instance->resource_ids[3] = IPSO_RR_ID_APPLICATION_TYPE; +} + +void ipso_instance_generic_sensor_init(ipso_generic_sensor_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_generic_sensor_t); + + p_instance->proto.object_id = IPSO_SO_ID_GENERIC_SENSOR; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_generic_sensor_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[6] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[7] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[8] = LWM2M_OPERATION_CODE_READ; + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_SENSOR_VALUE; + p_instance->resource_ids[1] = IPSO_RR_ID_SENSOR_UNITS; + p_instance->resource_ids[2] = IPSO_RR_ID_MIN_MEASURED_VALUE; + p_instance->resource_ids[3] = IPSO_RR_ID_MAX_MEASURED_VALUE; + p_instance->resource_ids[4] = IPSO_RR_ID_MIN_RANGE_VALUE; + p_instance->resource_ids[5] = IPSO_RR_ID_MAX_RANGE_VALUE; + p_instance->resource_ids[6] = IPSO_RR_ID_RESET_MIN_MAX_MEASURED_VALUES; + p_instance->resource_ids[7] = IPSO_RR_ID_APPLICATION_TYPE; + p_instance->resource_ids[8] = IPSO_RR_ID_SENSOR_TYPE; +} + +void ipso_instance_illuminance_init(ipso_illuminance_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_illuminance_t); + + p_instance->proto.object_id = IPSO_SO_ID_ILLUMINANCE_SENSOR; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_illuminance_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[6] = LWM2M_OPERATION_CODE_EXECUTE; + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_SENSOR_VALUE; + p_instance->resource_ids[1] = IPSO_RR_ID_SENSOR_UNITS; + p_instance->resource_ids[2] = IPSO_RR_ID_MIN_MEASURED_VALUE; + p_instance->resource_ids[3] = IPSO_RR_ID_MAX_MEASURED_VALUE; + p_instance->resource_ids[4] = IPSO_RR_ID_MIN_RANGE_VALUE; + p_instance->resource_ids[5] = IPSO_RR_ID_MAX_RANGE_VALUE; + p_instance->resource_ids[6] = IPSO_RR_ID_RESET_MIN_MAX_MEASURED_VALUES; +} + +void ipso_instance_presence_init(ipso_presence_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_presence_t); + + p_instance->proto.object_id = IPSO_SO_ID_PRESENCE_SENSOR; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_presence_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[5] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_DIGITAL_INPUT_STATE; + p_instance->resource_ids[1] = IPSO_RR_ID_DIGITAL_INPUT_COUNTER; + p_instance->resource_ids[2] = IPSO_RR_ID_DIGITAL_INPUT_COUNTER_RESET; + p_instance->resource_ids[3] = IPSO_RR_ID_SENSOR_TYPE; + p_instance->resource_ids[4] = IPSO_RR_ID_BUSY_TO_CLEAR_DELAY; + p_instance->resource_ids[5] = IPSO_RR_ID_CLEAR_TO_BUSY_DELAY; +} + +void ipso_instance_temperature_init(ipso_temperature_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_temperature_t); + + p_instance->proto.object_id = IPSO_SO_ID_TEMPERATURE_SENSOR; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_temperature_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[6] = LWM2M_OPERATION_CODE_EXECUTE; + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_SENSOR_VALUE; + p_instance->resource_ids[1] = IPSO_RR_ID_SENSOR_UNITS; + p_instance->resource_ids[2] = IPSO_RR_ID_MIN_MEASURED_VALUE; + p_instance->resource_ids[3] = IPSO_RR_ID_MAX_MEASURED_VALUE; + p_instance->resource_ids[4] = IPSO_RR_ID_MIN_RANGE_VALUE; + p_instance->resource_ids[5] = IPSO_RR_ID_MAX_RANGE_VALUE; + p_instance->resource_ids[6] = IPSO_RR_ID_RESET_MIN_MAX_MEASURED_VALUES; +} + +void ipso_instance_humidity_init(ipso_humidity_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_humidity_t); + + p_instance->proto.object_id = IPSO_SO_ID_HUMIDITY_SENSOR; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_humidity_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[6] = LWM2M_OPERATION_CODE_EXECUTE; + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_SENSOR_VALUE; + p_instance->resource_ids[1] = IPSO_RR_ID_SENSOR_UNITS; + p_instance->resource_ids[2] = IPSO_RR_ID_MIN_MEASURED_VALUE; + p_instance->resource_ids[3] = IPSO_RR_ID_MAX_MEASURED_VALUE; + p_instance->resource_ids[4] = IPSO_RR_ID_MIN_RANGE_VALUE; + p_instance->resource_ids[5] = IPSO_RR_ID_MAX_RANGE_VALUE; + p_instance->resource_ids[6] = IPSO_RR_ID_RESET_MIN_MAX_MEASURED_VALUES; +} + +void ipso_instance_power_measurement_init(ipso_power_measurement_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_power_measurement_t); + + p_instance->proto.object_id = IPSO_SO_ID_POWER_MEASUREMENT; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_power_measurement_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[6] = LWM2M_OPERATION_CODE_WRITE; + p_instance->operations[7] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[8] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[9] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[10] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[11] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[12] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[13] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[14] = LWM2M_OPERATION_CODE_WRITE; + p_instance->operations[15] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[16] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[17] = LWM2M_OPERATION_CODE_EXECUTE; + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_INSTANTANEOUS_ACTIVE_POWER; + p_instance->resource_ids[1] = IPSO_RR_ID_MIN_MEASURED_ACTIVE_POWER; + p_instance->resource_ids[2] = IPSO_RR_ID_MAX_MEASURED_ACTIVE_POWER; + p_instance->resource_ids[3] = IPSO_RR_ID_MIN_RANGE_ACTIVE_POWER; + p_instance->resource_ids[4] = IPSO_RR_ID_MAX_RANGE_ACTIVE_POWER; + p_instance->resource_ids[5] = IPSO_RR_ID_CUMULATIVE_ACTIVE_POWER; + p_instance->resource_ids[6] = IPSO_RR_ID_ACTIVE_POWER_CALIBRATION; + p_instance->resource_ids[7] = IPSO_RR_ID_INSTANTANEOUS_REACTIVE_POWER; + p_instance->resource_ids[8] = IPSO_RR_ID_MIN_MEASURED_REACTIVE_POWER; + p_instance->resource_ids[9] = IPSO_RR_ID_MAX_MEASURED_REACTIVE_POWER; + p_instance->resource_ids[10] = IPSO_RR_ID_MIN_RANGE_REACTIVE_POWER; + p_instance->resource_ids[11] = IPSO_RR_ID_MAX_RANGE_REACTIVE_POWER; + p_instance->resource_ids[12] = IPSO_RR_ID_RESET_MIN_MAX_MEASURED_VALUES; + p_instance->resource_ids[13] = IPSO_RR_ID_CUMULATIVE_REACTIVE_POWER; + p_instance->resource_ids[14] = IPSO_RR_ID_REACTIVE_POWER_CALIBRATION; + p_instance->resource_ids[15] = IPSO_RR_ID_POWER_FACTOR; + p_instance->resource_ids[16] = IPSO_RR_ID_CURRENT_CALIBRATION; + p_instance->resource_ids[17] = IPSO_RR_ID_RESET_CUMULATIVE_ENERGY; +} + +void ipso_instance_actuation_init(ipso_actuation_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_actuation_t); + + p_instance->proto.object_id = IPSO_SO_ID_ACTUATION; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_actuation_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[1] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[2] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[3] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[4] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_ON_OFF; + p_instance->resource_ids[1] = IPSO_RR_ID_DIMMER; + p_instance->resource_ids[2] = IPSO_RR_ID_ON_TIME; + p_instance->resource_ids[3] = IPSO_RR_ID_MULTI_STATE_OUTPUT; + p_instance->resource_ids[4] = IPSO_RR_ID_APPLICATION_TYPE; +} + +void ipso_instance_set_point_init(ipso_set_point_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_set_point_t); + + p_instance->proto.object_id = IPSO_SO_ID_SET_POINT; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_set_point_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[1] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_SETPOINT_VALUE; + p_instance->resource_ids[1] = IPSO_RR_ID_COLOUR; + p_instance->resource_ids[2] = IPSO_RR_ID_SENSOR_UNITS; + p_instance->resource_ids[3] = IPSO_RR_ID_APPLICATION_TYPE; +} + +void ipso_instance_load_control_init(ipso_load_control_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_load_control_t); + + p_instance->proto.object_id = IPSO_SO_ID_LOAD_CONTROL; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_load_control_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[1] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[2] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[3] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[4] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[5] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_EVENT_IDENTIFIER; + p_instance->resource_ids[1] = IPSO_RR_ID_START_TIME; + p_instance->resource_ids[2] = IPSO_RR_ID_DURATION_IN_MIN; + p_instance->resource_ids[3] = IPSO_RR_ID_CRITICALITY_LEVEL; + p_instance->resource_ids[4] = IPSO_RR_ID_AVG_LOAD_ADJPCT; + p_instance->resource_ids[5] = IPSO_RR_ID_DUTY_CYCLE; +} + +void ipso_instance_light_control_init(ipso_light_control_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_light_control_t); + + p_instance->proto.object_id = IPSO_SO_ID_LIGHT_CONTROL; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_light_control_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[1] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[2] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[6] = LWM2M_OPERATION_CODE_READ; + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_ON_OFF; + p_instance->resource_ids[1] = IPSO_RR_ID_DIMMER; + p_instance->resource_ids[2] = IPSO_RR_ID_COLOUR; + p_instance->resource_ids[3] = IPSO_RR_ID_SENSOR_UNITS; + p_instance->resource_ids[4] = IPSO_RR_ID_ON_TIME; + p_instance->resource_ids[5] = IPSO_RR_ID_CUMULATIVE_ACTIVE_POWER; + p_instance->resource_ids[6] = IPSO_RR_ID_POWER_FACTOR; +} + +void ipso_instance_power_control_init(ipso_power_control_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_power_control_t); + + p_instance->proto.object_id = IPSO_SO_ID_POWER_CONTROL; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_power_control_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[1] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[2] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_ON_OFF; + p_instance->resource_ids[1] = IPSO_RR_ID_DIMMER; + p_instance->resource_ids[2] = IPSO_RR_ID_ON_TIME; + p_instance->resource_ids[3] = IPSO_RR_ID_CUMULATIVE_ACTIVE_POWER; + p_instance->resource_ids[4] = IPSO_RR_ID_POWER_FACTOR; +} + +void ipso_instance_accelerometer_init(ipso_accelerometer_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_accelerometer_t); + + p_instance->proto.object_id = IPSO_SO_ID_ACCELEROMETER; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_accelerometer_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_X_VALUE; + p_instance->resource_ids[1] = IPSO_RR_ID_Y_VALUE; + p_instance->resource_ids[2] = IPSO_RR_ID_Z_VALUE; + p_instance->resource_ids[3] = IPSO_RR_ID_SENSOR_UNITS; + p_instance->resource_ids[4] = IPSO_RR_ID_MIN_RANGE_VALUE; + p_instance->resource_ids[5] = IPSO_RR_ID_MAX_RANGE_VALUE; +} + +void ipso_instance_magnetometer_init(ipso_magnetometer_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_magnetometer_t); + + p_instance->proto.object_id = IPSO_SO_ID_MAGNETOMETER; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_magnetometer_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + + // Set resource IDs. + p_instance->resource_ids[0] = IPSO_RR_ID_X_VALUE; + p_instance->resource_ids[1] = IPSO_RR_ID_Y_VALUE; + p_instance->resource_ids[2] = IPSO_RR_ID_Z_VALUE; + p_instance->resource_ids[3] = IPSO_RR_ID_SENSOR_UNITS; + p_instance->resource_ids[4] = IPSO_RR_ID_COMPASS_DIRECTION; +} + +void ipso_instance_barometer_init(ipso_barometer_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, ipso_barometer_t); + + p_instance->proto.object_id = IPSO_SO_ID_BAROMETER; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((ipso_barometer_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[6] = LWM2M_OPERATION_CODE_EXECUTE; + + // Set resource IDs. + + + p_instance->resource_ids[0] = IPSO_RR_ID_SENSOR_VALUE; + p_instance->resource_ids[1] = IPSO_RR_ID_SENSOR_UNITS; + p_instance->resource_ids[2] = IPSO_RR_ID_MIN_MEASURED_VALUE; + p_instance->resource_ids[3] = IPSO_RR_ID_MAX_MEASURED_VALUE; + p_instance->resource_ids[4] = IPSO_RR_ID_MIN_RANGE_VALUE; + p_instance->resource_ids[5] = IPSO_RR_ID_MAX_RANGE_VALUE; + p_instance->resource_ids[6] = IPSO_RR_ID_RESET_MIN_MAX_MEASURED_VALUES; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects.h new file mode 100644 index 0000000..7a9ca8e --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects.h @@ -0,0 +1,628 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file lwm2m_objects.h + * + * @defgroup iot_sdk_ipso_objects IPSO Smart Object definititions and types + * @ingroup iot_sdk_lwm2m + * @{ + * @brief IPSO objects definitions and types. + * + * @note The definitions used in this module are from the IPSO Alliance + * "IPSO SmartOject Guideline - Smart Objects Starter Pack1.0". + * The specification could be found at http://www.ipso-alliance.org/. + */ + +#ifndef IPSO_OBJECTS_H__ +#define IPSO_OBJECTS_H__ + +#include "lwm2m_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IPSO_SO_ID_DIGITAL_INPUT 3200 +#define IPSO_SO_ID_DIGITAL_OUTPUT 3201 +#define IPSO_SO_ID_ANALOGUE_INPUT 3202 +#define IPSO_SO_ID_ANALOGUE_OUTPUT 3203 +#define IPSO_SO_ID_GENERIC_SENSOR 3300 +#define IPSO_SO_ID_ILLUMINANCE_SENSOR 3301 +#define IPSO_SO_ID_PRESENCE_SENSOR 3302 +#define IPSO_SO_ID_TEMPERATURE_SENSOR 3303 +#define IPSO_SO_ID_HUMIDITY_SENSOR 3304 +#define IPSO_SO_ID_POWER_MEASUREMENT 3305 +#define IPSO_SO_ID_ACTUATION 3306 +#define IPSO_SO_ID_SET_POINT 3308 +#define IPSO_SO_ID_LOAD_CONTROL 3310 +#define IPSO_SO_ID_LIGHT_CONTROL 3311 +#define IPSO_SO_ID_POWER_CONTROL 3312 +#define IPSO_SO_ID_ACCELEROMETER 3313 +#define IPSO_SO_ID_MAGNETOMETER 3314 +#define IPSO_SO_ID_BAROMETER 3315 + +/** @brief IPSO Reusable Resource IDs (Section 21). */ +#define IPSO_RR_ID_DIGITAL_INPUT_STATE 5500 +#define IPSO_RR_ID_DIGITAL_INPUT_COUNTER 5501 +#define IPSO_RR_ID_DIGITAL_INPUT_POLARITY 5502 +#define IPSO_RR_ID_DIGITAL_INPUT_DEBOUNCE_PERIOD 5503 +#define IPSO_RR_ID_DIGITAL_INPUT_EDGE_SELECTION 5504 +#define IPSO_RR_ID_DIGITAL_INPUT_COUNTER_RESET 5505 + +#define IPSO_RR_ID_DIGITAL_OUTPUT_STATE 5550 +#define IPSO_RR_ID_DIGITAL_OUTPUT_POLARITY 5551 + + +// Digital output polarity options. +#define IPSO_RR_ID_DIGITAL_OUTPUT_POLARITY_NORMAL 0 +#define IPSO_RR_ID_DIGITAL_OUTPUT_POLARITY_REVERSED 1 + +#define IPSO_RR_ID_ANALOG_INPUT_CURRENT_VALUE 5600 +#define IPSO_RR_ID_MIN_MEASURED_VALUE 5601 +#define IPSO_RR_ID_MAX_MEASURED_VALUE 5602 +#define IPSO_RR_ID_MIN_RANGE_VALUE 5603 +#define IPSO_RR_ID_MAX_RANGE_VALUE 5604 +#define IPSO_RR_ID_RESET_MIN_MAX_MEASURED_VALUES 5605 + +#define IPSO_RR_ID_ANALOG_OUTPUT_CURRENT_VALUE 5650 + +#define IPSO_RR_ID_SENSOR_VALUE 5700 +#define IPSO_RR_ID_SENSOR_UNITS 5701 +#define IPSO_RR_ID_X_VALUE 5702 +#define IPSO_RR_ID_Y_VALUE 5703 +#define IPSO_RR_ID_Z_VALUE 5704 +#define IPSO_RR_ID_COMPASS_DIRECTION 5705 +#define IPSO_RR_ID_COLOUR 5706 + +#define IPSO_RR_ID_APPLICATION_TYPE 5750 +#define IPSO_RR_ID_SENSOR_TYPE 5751 + +#define IPSO_RR_ID_INSTANTANEOUS_ACTIVE_POWER 5800 +#define IPSO_RR_ID_MIN_MEASURED_ACTIVE_POWER 5801 +#define IPSO_RR_ID_MAX_MEASURED_ACTIVE_POWER 5802 +#define IPSO_RR_ID_MIN_RANGE_ACTIVE_POWER 5803 +#define IPSO_RR_ID_MAX_RANGE_ACTIVE_POWER 5804 +#define IPSO_RR_ID_CUMULATIVE_ACTIVE_POWER 5805 +#define IPSO_RR_ID_ACTIVE_POWER_CALIBRATION 5806 + +#define IPSO_RR_ID_INSTANTANEOUS_REACTIVE_POWER 5810 +#define IPSO_RR_ID_MIN_MEASURED_REACTIVE_POWER 5811 +#define IPSO_RR_ID_MAX_MEASURED_REACTIVE_POWER 5812 +#define IPSO_RR_ID_MIN_RANGE_REACTIVE_POWER 5813 +#define IPSO_RR_ID_MAX_RANGE_REACTIVE_POWER 5814 +#define IPSO_RR_ID_CUMULATIVE_REACTIVE_POWER 5815 +#define IPSO_RR_ID_REACTIVE_POWER_CALIBRATION 5816 + +#define IPSO_RR_ID_POWER_FACTOR 5820 +#define IPSO_RR_ID_CURRENT_CALIBRATION 5821 +#define IPSO_RR_ID_RESET_CUMULATIVE_ENERGY 5822 +#define IPSO_RR_ID_EVENT_IDENTIFIER 5823 +#define IPSO_RR_ID_START_TIME 5824 +#define IPSO_RR_ID_DURATION_IN_MIN 5825 +#define IPSO_RR_ID_CRITICALITY_LEVEL 5826 +#define IPSO_RR_ID_AVG_LOAD_ADJPCT 5827 +#define IPSO_RR_ID_DUTY_CYCLE 5828 + +#define IPSO_RR_ID_ON_OFF 5850 +#define IPSO_RR_ID_DIMMER 5851 +#define IPSO_RR_ID_ON_TIME 5852 +#define IPSO_RR_ID_MULTI_STATE_OUTPUT 5853 + +#define IPSO_RR_ID_SETPOINT_VALUE 5900 + +#define IPSO_RR_ID_BUSY_TO_CLEAR_DELAY 5903 +#define IPSO_RR_ID_CLEAR_TO_BUSY_DELAY 5904 + + + +/* Digital input ID: 3200 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[8]; /* Internal. */ + uint16_t resource_ids[8]; /* Internal. */ + + /* Public members. */ + bool state; + uint32_t counter; + bool polarity; + uint32_t debounce_period; /* Unit: milliseconds */ + uint8_t edge_selection; /* Range: 1-3 */ + /* Counter reset is execute only */ + lwm2m_string_t application_type; + lwm2m_string_t sensor_type; + +} ipso_digital_input_t; + + +/* Digital output ID: 3201 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. MUST be first. */ + uint8_t operations[3]; /* Internal. MUST be second. */ + uint16_t resource_ids[3]; /* Internal. MUST be third. */ + + /* Public members. */ + bool digital_output_state; + bool digital_output_polarity; + lwm2m_string_t application_type; + +} ipso_digital_output_t; + +/* Analog input ID: 3202 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[8]; /* Internal. */ + uint16_t resource_ids[8]; /* Internal. */ + + /* Public members. */ + float current_value; + float min_measured_value; + float max_measured_value; + float min_range_value; + float max_range_value; + /* Reset min and max measured values is execute only */ + lwm2m_string_t application_type; + lwm2m_string_t sensor_type; +} ipso_analog_input_t; + +/* Analog output ID: 3203 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[4]; /* Internal. */ + uint16_t resource_ids[4]; /* Internal. */ + + /* Public members. */ + float current_value; + float min_range_value; + float max_range_value; + lwm2m_string_t application_type; + +} ipso_analog_output_t; + +/* Generic sensor ID: 3300 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[9]; /* Internal. */ + uint16_t resource_ids[9]; /* Internal. */ + + /* Public members. */ + float sensor_value; + lwm2m_string_t units; + float min_measured_value; + float max_measured_value; + float min_range_value; + float max_range_value; + /* Reset min and max measured values is execute only */ + lwm2m_string_t application_type; + lwm2m_string_t sensor_type; + +} ipso_generic_sensor_t; + +/* Illuminance ID: 3301 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[7]; /* Internal. */ + uint16_t resource_ids[7]; /* Internal. */ + + /* Public members. */ + float sensor_value; + lwm2m_string_t units; + float min_measured_value; + float max_measured_value; + float min_range_value; + float max_range_value; + /* Reset min and max measured values is execute only */ + +} ipso_illuminance_t; + +/* Presence ID: 3302 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[8]; /* Internal. */ + uint16_t resource_ids[8]; /* Internal. */ + + /* Public members. */ + bool digital_input_state; + uint32_t digital_input_counter; + /* Digital input counter reset is execute only */ + lwm2m_string_t sensor_type; + uint32_t busy_to_clear_delay; // Unit: ms + uint32_t clear_to_busy_delay; // Unit: ms + + +} ipso_presence_t; + +/* Temperature ID: 3303 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[7]; /* Internal. */ + uint16_t resource_ids[7]; /* Internal. */ + + /* Public members. */ + float sensor_value; + lwm2m_string_t units; + float min_measured_value; + float max_measured_value; + float min_range_value; + float max_range_value; + /* Reset min and max measured values is execute only */ + + +} ipso_temperature_t; + +/* Humidity ID: 3304 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[8]; /* Internal. */ + uint16_t resource_ids[8]; /* Internal. */ + + /* Public members. */ + float sensor_value; + lwm2m_string_t units; + float min_measured_value; + float max_measured_value; + float min_range_value; + float max_range_value; + /* Reset min and max measured values is execute only */ + +} ipso_humidity_t; + +/* Power measurement ID: 3305 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[18]; /* Internal. */ + uint16_t resource_ids[18]; /* Internal. */ + + /* Public members. */ + float instantaneous_active_power; + float min_measured_active_power; + float max_measured_active_power; + float min_range_active_power; + float max_range_active_power; + float cumulative_active_power; + float active_power_calibration; + float instantaneous_reactive_power; + float min_measured_reactive_power; + float max_measured_reactive_power; + float min_range_reactive_power; + float max_range_reactive_power; + /* Reset min and max measured values is execute only */ + float cumulative_reactive_power; + float reactive_power_calibration; + float power_factor; + float current_calibration; + /* Reset cumulative is execute only */ + +} ipso_power_measurement_t; + +/* Actuation ID: 3306 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[5]; /* Internal. */ + uint16_t resource_ids[5]; /* Internal. */ + + /* Public members. */ + bool on; + uint16_t dimmer; // Unit: % Range: 0 - 100 + uint32_t on_time; // Unit: s + lwm2m_string_t multi_state_output; + lwm2m_string_t application_type; + +} ipso_actuation_t; + +/* Set point ID: 3308 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[4]; /* Internal. */ + uint16_t resource_ids[4]; /* Internal. */ + + /* Public members. */ + float set_point_value; + lwm2m_string_t colour; + lwm2m_string_t units; + lwm2m_string_t application_type; + +} ipso_set_point_t; + +/* Load control ID: 3310 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[6]; /* Internal. */ + uint16_t resource_ids[6]; /* Internal. */ + + /* Public members. */ + lwm2m_string_t event_identifier; + lwm2m_time_t start_time; + uint32_t duration_in_min; + uint8_t criticality_level; // Range: 0-3 + uint16_t avg_load_adjpct; // Unit: % range: 0-100 + uint16_t duty_cycle; // Unit: % range: 0-100 + + +} ipso_load_control_t; + +/* Light control ID: 3311 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[7]; /* Internal. */ + uint16_t resource_ids[7]; /* Internal. */ + + /* Public members. */ + bool on; + uint16_t dimmer; // Unit: % Range: 0 - 100 + lwm2m_string_t colour; + lwm2m_string_t units; + uint32_t on_time; // Unit: s + float cumulative_active_power; + float power_factor; + +} ipso_light_control_t; + +/* Power control ID: 3312 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[5]; /* Internal. */ + uint16_t resource_ids[5]; /* Internal. */ + + /* Public members. */ + bool on; + uint16_t dimmer; // Unit: % Range: 0 - 100 + uint32_t on_time; // Unit: s + float cumulative_active_power; + float power_factor; + +} ipso_power_control_t; + +/* Accelerometer ID: 3313 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[6]; /* Internal. */ + uint16_t resource_ids[6]; /* Internal. */ + + /* Public members. */ + float x_value; + float y_value; + float z_value; + lwm2m_string_t units; + float min_range_value; + float max_range_value; + +} ipso_accelerometer_t; + +/* Magnetometer ID: 3314 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[8]; /* Internal. */ + uint16_t resource_ids[8]; /* Internal. */ + + /* Public members. */ + float x_value; + float y_value; + float z_value; + lwm2m_string_t units; + float compass_direction; // Unit: deg Range: 0-360 + +} ipso_magnetometer_t; + +/* Barometer ID: 3315 */ +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. */ + uint8_t operations[7]; /* Internal. */ + uint16_t resource_ids[7]; /* Internal. */ + + /* Public members. */ + float sensor_value; + lwm2m_string_t units; + float min_measured_value; + float max_measured_value; + float min_range_value; + float max_range_value; + /* Reset min and max measured values is execute only */ + +} ipso_barometer_t; + + +/**@brief Initialize an IPSO digital input object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_digital_input_init(ipso_digital_input_t * p_instance); + +/**@brief Initialize an IPSO digital output object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_digital_output_init(ipso_digital_output_t * p_instance); + +/**@brief Initialize an IPSO analog input object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_analog_input_init(ipso_analog_input_t * p_instance); + +/**@brief Initialize an IPSO analog output object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_analog_output_init(ipso_analog_output_t * p_instance); + +/**@brief Initialize an IPSO generic sensor object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_generic_sensor_init(ipso_generic_sensor_t * p_instance); + +/**@brief Initialize an IPSO illuminance object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_illuminance_init(ipso_illuminance_t * p_instance); + +/**@brief Initialize an IPSO presence object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_presence_init(ipso_presence_t * p_instance); + +/**@brief Initialize an IPSO temperature object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_temperature_init(ipso_temperature_t * p_instance); + +/**@brief Initialize an IPSO humidity object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_humidity_init(ipso_humidity_t * p_instance); + +/**@brief Initialize an IPSO power measurement object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_power_measurement_init(ipso_power_measurement_t * p_instance); + +/**@brief Initialize an IPSO actuation object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_actuation_init(ipso_actuation_t * p_instance); + +/**@brief Initialize an IPSO set point object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_set_point_init(ipso_set_point_t * p_instance); + +/**@brief Initialize an IPSO load control object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_load_control_init(ipso_load_control_t * p_instance); + +/**@brief Initialize an IPSO light control object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_light_control_init(ipso_light_control_t * p_instance); + +/**@brief Initialize an IPSO power control object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_power_control_init(ipso_power_control_t * p_instance); + +/**@brief Initialize an IPSO accelerometer object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_accelerometer_init(ipso_accelerometer_t * p_instance); + +/**@brief Initialize an IPSO magnetometer object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_magnetometer_init(ipso_magnetometer_t * p_instance); + +/**@brief Initialize an IPSO barometer object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void ipso_instance_barometer_init(ipso_barometer_t * p_instance); + +#ifdef __cplusplus +} +#endif + +#endif // LWM2M_OBJECTS_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects_tlv.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects_tlv.c new file mode 100644 index 0000000..5cb53ec --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects_tlv.c @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "ipso_objects_tlv.h" +#include "lwm2m_tlv.h" + +static void index_buffer_len_update(uint32_t * index, uint32_t * buffer_len, uint32_t max_buffer) +{ + *index += *buffer_len; + *buffer_len = max_buffer - *index; +} + +uint32_t ipso_tlv_ipso_digital_output_decode(ipso_digital_output_t * p_digital_output, + uint8_t * p_buffer, + uint32_t buffer_len) +{ + uint32_t err_code; + lwm2m_tlv_t tlv; + + uint32_t index = 0; + + while (index < buffer_len) + { + err_code = lwm2m_tlv_decode(&tlv, &index, p_buffer, buffer_len); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + switch (tlv.id) + { + case IPSO_RR_ID_DIGITAL_OUTPUT_STATE: + { + p_digital_output->digital_output_state = tlv.value[0]; + break; + } + + case IPSO_RR_ID_DIGITAL_OUTPUT_POLARITY: + { + p_digital_output->digital_output_polarity = tlv.value[0]; + break; + } + + case IPSO_RR_ID_APPLICATION_TYPE: + { + p_digital_output->application_type.p_val = (char *)tlv.value; + p_digital_output->application_type.len = tlv.length; + break; + } + + default: + break; + } + } + + return NRF_SUCCESS; +} + +uint32_t ipso_tlv_ipso_digital_output_encode(uint8_t * p_buffer, + uint32_t * p_buffer_len, + ipso_digital_output_t * p_digital_output) +{ + uint32_t err_code; + uint32_t max_buffer = *p_buffer_len; + uint32_t index = 0; + + lwm2m_tlv_t tlv; + tlv.id_type = TLV_TYPE_RESOURCE_VAL; // type is the same for all. + + // Encode state. + lwm2m_tlv_bool_set(&tlv, p_digital_output->digital_output_state, IPSO_RR_ID_DIGITAL_OUTPUT_STATE); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + // Encode polarity. + lwm2m_tlv_bool_set(&tlv, p_digital_output->digital_output_polarity, IPSO_RR_ID_DIGITAL_OUTPUT_POLARITY); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + // Encode application type. + lwm2m_tlv_string_set(&tlv, p_digital_output->application_type, IPSO_RR_ID_APPLICATION_TYPE); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + *p_buffer_len = index; + + return NRF_SUCCESS; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects_tlv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects_tlv.h new file mode 100644 index 0000000..e2c1228 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/ipso_objects_tlv.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file lwm2m_objects_tlv.h + * + * @defgroup iot_sdk_ipso_objects_tlv IPSO Smart Object TLV encoder and decoder API + * @ingroup iot_sdk_lwm2m + * @{ + * @brief IPSO Smart Object TLV encoder and decoder API. + */ + +#ifndef IPSO_OBJECTS_TLV_H__ +#define IPSO_OBJECTS_TLV_H__ + +#include <stdint.h> +#include "ipso_objects.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Decode an IPSO digital output object from a TLV byte buffer. + * + * @note Resource values NOT found in the tlv will not be altered. + * + * @warning lwm2m_string_t and lwm2m_opaque_t values will point to the byte buffer and needs + * to be copied by the application before the byte buffer is freed. + * + * @param[out] p_digital_output Pointer to a LWM2M server object to be filled by the decoded TLVs. + * @param[in] p_buffer Pointer to the TLV byte buffer to be decoded. + * @param[in] buffer_len Size of the buffer to be decoded. + * + * @retval NRF_SUCCESS If decoding was successfull. + */ +uint32_t ipso_tlv_ipso_digital_output_decode(ipso_digital_output_t * p_digital_output, + uint8_t * p_buffer, + uint32_t buffer_len); + +/**@brief Encode an IPSO digital output object to a TLV byte buffer. + * + * @param[out] p_buffer Pointer to a byte buffer to be used to fill the encoded TLVs. + * @param[inout] p_buffer_len Value by reference inicating the size of the buffer provided. + * Will return the number of used bytes on return. + * @param[in] p_digital_output Pointer to the IPSO digital output object to be encoded into TLVs. + * + * @retval NRF_SUCCESS If the encoded was successfull. + */ +uint32_t ipso_tlv_ipso_digital_output_encode(uint8_t * p_buffer, + uint32_t * p_buffer_len, + ipso_digital_output_t * p_digital_output); + +#ifdef __cplusplus +} +#endif + +#endif // IPSO_OBJECTS_TLV_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m.c new file mode 100644 index 0000000..bc022f1 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m.c @@ -0,0 +1,885 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> + +#include "lwm2m_api.h" +#include "lwm2m_register.h" +#include "lwm2m_bootstrap.h" +#include "sdk_os.h" +#include "lwm2m.h" +#include "sdk_config.h" + +#if LWM2M_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME lwm2m + +#define NRF_LOG_LEVEL LWM2M_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR LWM2M_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR LWM2M_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define LWM2M_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define LWM2M_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define LWM2M_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define LWM2M_ENTRY() LWM2M_TRC(">> %s", __func__) +#define LWM2M_EXIT() LWM2M_TRC("<< %s", __func__) + +#else // LWM2M_CONFIG_LOG_ENABLED + +#define LWM2M_TRC(...) /**< Disables traces. */ +#define LWM2M_DUMP(...) /**< Disables dumping of octet streams. */ +#define LWM2M_ERR(...) /**< Disables error logs. */ + +#define LWM2M_ENTRY(...) +#define LWM2M_EXIT(...) + +#endif // LWM2M_CONFIG_LOG_ENABLED + +#if (LWM2M_CONFIG_LOG_ENABLED != 0) + +static uint8_t op_desc_idx_lookup(uint8_t bitmask) +{ + for (uint8_t i = 0; i < 8; i++) + { + if ((bitmask > i) == 0x1) + { + return i; + } + } + + // If no bits where set in the bitmask. + return 0; +} + +static const char m_operation_desc[8][9] = { + "NONE", + "READ", + "WRITE", + "EXECUTE", + "DELETE", + "CREATE", + "DISCOVER", + "OBSERVE" +}; + +#endif + +SDK_MUTEX_DEFINE(m_lwm2m_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */ + +static lwm2m_object_prototype_t * m_objects[LWM2M_COAP_HANDLER_MAX_OBJECTS]; +static lwm2m_instance_prototype_t * m_instances[LWM2M_COAP_HANDLER_MAX_INSTANCES]; +static uint16_t m_num_objects; +static uint16_t m_num_instances; + +static void coap_error_handler(uint32_t error_code, coap_message_t * p_message) +{ + LWM2M_ERR("[CoAP]: Unhandled coap message recieved. Error code: %lu", error_code); +} + +static void internal_coap_handler_init(void) +{ + memset(m_objects, 0, sizeof(m_objects)); + memset(m_instances, 0, sizeof(m_instances)); + + m_num_objects = 0; + m_num_instances = 0; +} + + +static bool numbers_only(const char * p_str, uint16_t str_len) +{ + for (uint16_t i = 0; i < str_len; i++) + { + if (isdigit(p_str[i]) == 0) + { + return false; + } + } + return true; +} + + +static uint32_t instance_resolve(lwm2m_instance_prototype_t ** p_instance, + uint16_t object_id, + uint16_t instance_id) +{ + for (int i = 0; i < m_num_instances; ++i) + { + if (m_instances[i]->object_id == object_id && + m_instances[i]->instance_id == instance_id) + { + if (m_instances[i]->callback == NULL) + { + return NRF_ERROR_NULL; + } + + *p_instance = m_instances[i]; + + return NRF_SUCCESS; + } + } + + return NRF_ERROR_NOT_FOUND; +} + + +static uint32_t object_resolve(lwm2m_object_prototype_t ** p_instance, + uint16_t object_id) +{ + for (int i = 0; i < m_num_objects; ++i) + { + if (m_objects[i]->object_id == object_id) + { + if (m_objects[i]->callback == NULL) + { + return NRF_ERROR_NULL; + } + + *p_instance = m_objects[i]; + + return NRF_SUCCESS; + } + } + + return NRF_ERROR_NOT_FOUND; +} + +static uint32_t op_code_resolve(lwm2m_instance_prototype_t * p_instance, + uint16_t resource_id, + uint8_t * operation) +{ + uint8_t * operations = (uint8_t *) p_instance + p_instance->operations_offset; + uint16_t * operations_ids = (uint16_t *)((uint8_t *) p_instance + + p_instance->resource_ids_offset); + + for (int j = 0; j < p_instance->num_resources; ++j) + { + if (operations_ids[j] == resource_id) + { + *operation = operations[j]; + return NRF_SUCCESS; + } + } + + return NRF_ERROR_NOT_FOUND; +} + +static uint32_t internal_request_handle(coap_message_t * p_request, + uint16_t * p_path, + uint8_t path_len) +{ + uint32_t err_code; + uint8_t operation = LWM2M_OPERATION_CODE_NONE; + uint32_t content_type = 0; + + err_code = coap_message_ct_mask_get(p_request, &content_type); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + /** + * TODO: the methods should check if we have read / write / execute rights + * through ACL and resource operations + */ + + switch (p_request->header.code) + { + case COAP_CODE_GET: + { + LWM2M_TRC("[CoAP]: CoAP GET request"); + if (content_type == COAP_CT_APP_LINK_FORMAT) // Discover + { + operation = LWM2M_OPERATION_CODE_DISCOVER; + } + else // Read + { + operation = LWM2M_OPERATION_CODE_READ; + } + break; + } + + case COAP_CODE_PUT: + { + operation = LWM2M_OPERATION_CODE_WRITE; + break; + } + + case COAP_CODE_POST: + { + operation = LWM2M_OPERATION_CODE_WRITE; + break; + } + + case COAP_CODE_DELETE: + { + operation = LWM2M_OPERATION_CODE_DELETE; + break; + } + + default: + break; // Maybe send response with unsupported method not allowed? + } + + err_code = NRF_ERROR_NOT_FOUND; + + switch (path_len) + { + case 0: + { + if (operation == LWM2M_OPERATION_CODE_DELETE) + { + LWM2M_TRC("[CoAP]: >> %s root /", + m_operation_desc[op_desc_idx_lookup(operation)]); + + LWM2M_MUTEX_UNLOCK(); + + err_code = lwm2m_coap_handler_root(LWM2M_OPERATION_CODE_DELETE, p_request); + + LWM2M_MUTEX_LOCK(); + + LWM2M_TRC("[CoAP]: << %s root /", + m_operation_desc[op_desc_idx_lookup(operation)]); + } + break; + } + + case 1: + { + LWM2M_TRC("[CoAP]: >> %s object /%u/", + m_operation_desc[op_desc_idx_lookup(operation)], + p_path[0]); + + lwm2m_object_prototype_t * p_object; + + err_code = object_resolve(&p_object, p_path[0]); + + if (err_code != NRF_SUCCESS) + { + break; + } + + LWM2M_MUTEX_UNLOCK(); + + err_code = p_object->callback(p_object, LWM2M_INVALID_INSTANCE, operation, p_request); + + LWM2M_MUTEX_LOCK(); + + LWM2M_TRC("[CoAP]: << %s object /%u/, result: %s", + m_operation_desc[op_desc_idx_lookup(operation)], + p_path[0], + (err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND"); + + break; + } + + case 2: + { + LWM2M_TRC("[CoAP]: >> %s instance /%u/%u/", + m_operation_desc[op_desc_idx_lookup(operation)], + p_path[0], + p_path[1]); + + lwm2m_instance_prototype_t * p_instance; + + err_code = instance_resolve(&p_instance, p_path[0], p_path[1]); + if (err_code == NRF_SUCCESS) + { + LWM2M_MUTEX_UNLOCK(); + + err_code = p_instance->callback(p_instance, LWM2M_INVALID_RESOURCE, operation, p_request); + + LWM2M_MUTEX_LOCK(); + + LWM2M_TRC("[CoAP]: << %s instance /%u/%u/, result: %s", + m_operation_desc[op_desc_idx_lookup(operation)], + p_path[0], + p_path[1], + (err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND"); + break; + } + + // Bootstrap can write to non-existing instances + if (err_code == NRF_ERROR_NOT_FOUND && + operation == LWM2M_OPERATION_CODE_WRITE && + p_request->header.code == COAP_CODE_PUT) + { + LWM2M_TRC("[CoAP]: >> %s object /%u/%u/", + m_operation_desc[op_desc_idx_lookup(operation)], + p_path[0], + p_path[1]); + + lwm2m_object_prototype_t * p_object; + + err_code = object_resolve(&p_object, p_path[0]); + + if (err_code != NRF_SUCCESS) + { + break; + } + + LWM2M_MUTEX_UNLOCK(); + + err_code = p_object->callback(p_object, p_path[1], operation, p_request); + + LWM2M_MUTEX_LOCK(); + + LWM2M_TRC("[CoAP]: << %s object /%u/%u/, result: %s", + m_operation_desc[op_desc_idx_lookup(operation)], + p_path[0], + p_path[1], + (err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND"); + } + + if (err_code == NRF_ERROR_NOT_FOUND && + operation == LWM2M_OPERATION_CODE_WRITE && + p_request->header.code == COAP_CODE_POST) + { + LWM2M_TRC("[CoAP]: >> CREATE object /%u/%u/", + p_path[0], + p_path[1]); + + lwm2m_object_prototype_t * p_object; + + err_code = object_resolve(&p_object, p_path[0]); + + if (err_code != NRF_SUCCESS) + { + break; + } + + LWM2M_MUTEX_UNLOCK(); + + err_code = p_object->callback(p_object, p_path[1], LWM2M_OPERATION_CODE_CREATE, p_request); + + LWM2M_MUTEX_LOCK(); + + LWM2M_TRC("[CoAP]: << CREATE object /%u/%u/, result: %s", + p_path[0], + p_path[1], + (err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND"); + break; + } + + break; + } + + case 3: + { + if (operation == LWM2M_OPERATION_CODE_DELETE) + { + // Deleting resources within an instance not allowed. + break; + } + + if (p_request->header.code == COAP_CODE_POST) + { + for (int i = 0; i < m_num_instances; ++i) + { + if ((m_instances[i]->object_id == p_path[0]) && + (m_instances[i]->instance_id == p_path[1])) + { + if (m_instances[i]->callback == NULL) + { + err_code = NRF_ERROR_NULL; + break; + } + + uint8_t resource_operation = 0; + err_code = op_code_resolve(m_instances[i], p_path[2], &resource_operation); + + if (err_code != NRF_SUCCESS) + break; + + if ((resource_operation & LWM2M_OPERATION_CODE_EXECUTE) > 0) + { + operation = LWM2M_OPERATION_CODE_EXECUTE; + } + + if ((resource_operation & LWM2M_OPERATION_CODE_WRITE) > 0) + { + operation = LWM2M_OPERATION_CODE_WRITE; + } + + LWM2M_TRC("[CoAP]: >> %s instance /%u/%u/%u/", + m_operation_desc[op_desc_idx_lookup(operation)], + m_instances[i]->object_id, + m_instances[i]->instance_id, + p_path[2]); + + LWM2M_MUTEX_UNLOCK(); + + (void)m_instances[i]->callback(m_instances[i], + p_path[2], + operation, + p_request); + + LWM2M_MUTEX_LOCK(); + + err_code = NRF_SUCCESS; + + LWM2M_TRC("[CoAP]: << %s instance /%u/%u/%u/", + m_operation_desc[op_desc_idx_lookup(operation)], + m_instances[i]->object_id, + m_instances[i]->instance_id, + p_path[2]); + + break; + } + } + } + else + { + LWM2M_TRC("[CoAP]: >> %s instance /%u/%u/%u/", + m_operation_desc[op_desc_idx_lookup(operation)], + p_path[0], + p_path[1], + p_path[2]); + + lwm2m_instance_prototype_t * p_instance; + + err_code = instance_resolve(&p_instance, p_path[0], p_path[1]); + if (err_code != NRF_SUCCESS) + { + break; + } + + LWM2M_MUTEX_UNLOCK(); + + err_code = p_instance->callback(p_instance, p_path[2], operation, p_request); + + LWM2M_MUTEX_LOCK(); + + LWM2M_TRC("[CoAP]: << %s instance /%u/%u/%u/, result: %s", + m_operation_desc[op_desc_idx_lookup(operation)], + p_path[0], + p_path[1], + p_path[2], + (err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND"); + } + break; + } + + default: + break; + } + + return err_code; +} + + +static uint32_t lwm2m_coap_handler_handle_request(coap_message_t * p_request) +{ + LWM2M_ENTRY(); + + uint16_t index; + uint16_t path[3]; + char * endptr; + + bool is_numbers_only = true; + uint16_t path_index = 0; + uint32_t err_code = NRF_SUCCESS; + + LWM2M_MUTEX_LOCK(); + + for (index = 0; index < p_request->options_count; index++) + { + if (p_request->options[index].number == COAP_OPT_URI_PATH) + { + uint16_t option_len = p_request->options[index].length; + bool numbers = numbers_only((char *)p_request->options[index].p_data, + option_len); + + if (numbers) + { + // Declare a temporary array that is 1 byte longer than the + // option data in order to leave space for a terminating character. + uint8_t option_data[option_len + 1]; + // Set the temporary array to zero. + memset(option_data, 0, sizeof(option_data)); + // Copy the option data string to the temporary array. + memcpy(option_data, p_request->options[index].p_data, option_len); + + // Convert the zero-terminated string to a long int value. + path[path_index] = strtol((char *)option_data, &endptr, 10); + + ++path_index; + + if (endptr == ((char *)option_data)) + { + err_code = NRF_ERROR_NOT_FOUND; + break; + } + + if (endptr != ((char *)option_data + option_len)) + { + err_code = NRF_ERROR_NOT_FOUND; + break; + } + } + else + { + is_numbers_only = false; + break; + } + } + } + + if (err_code == NRF_SUCCESS) + { + if (is_numbers_only == true) + { + err_code = internal_request_handle(p_request, path, path_index); + } + else + { + // If uri path did not consist of numbers only. + char * requested_uri = NULL; + for (index = 0; index < p_request->options_count; index++) + { + if (p_request->options[index].number == COAP_OPT_URI_PATH) + { + requested_uri = (char *)p_request->options[index].p_data; + + // Stop on first URI hit. + break; + } + } + + if (requested_uri == NULL) + { + err_code = NRF_ERROR_NOT_FOUND; + } + else + { + // Try to look up if there is a match with object with an alias name. + for (int i = 0; i < m_num_objects; ++i) + { + if (m_objects[i]->object_id == LWM2M_NAMED_OBJECT) + { + size_t size = strlen(m_objects[i]->p_alias_name); + if ((strncmp(m_objects[i]->p_alias_name, requested_uri, size) == 0)) + { + if (m_objects[i]->callback == NULL) + { + err_code = NRF_ERROR_NULL; + break; + } + + LWM2M_MUTEX_UNLOCK(); + + err_code = m_objects[i]->callback(m_objects[i], + LWM2M_INVALID_INSTANCE, + LWM2M_OPERATION_CODE_NONE, + p_request); + + LWM2M_MUTEX_LOCK(); + + break; + } + } + else + { + // This is not a name object, return error code. + err_code = NRF_ERROR_NOT_FOUND; + break; + } + } + } + } + } + + LWM2M_MUTEX_UNLOCK(); + + LWM2M_EXIT(); + + return err_code; +} + + +uint32_t lwm2m_coap_handler_instance_add(lwm2m_instance_prototype_t * p_instance) +{ + LWM2M_ENTRY(); + + NULL_PARAM_CHECK(p_instance); + + LWM2M_MUTEX_LOCK(); + + if (m_num_instances == LWM2M_COAP_HANDLER_MAX_INSTANCES) + { + LWM2M_MUTEX_UNLOCK(); + + return NRF_ERROR_NO_MEM; + } + + m_instances[m_num_instances] = p_instance; + ++m_num_instances; + + LWM2M_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t lwm2m_coap_handler_instance_delete(lwm2m_instance_prototype_t * p_instance) +{ + LWM2M_ENTRY(); + + NULL_PARAM_CHECK(p_instance); + + LWM2M_MUTEX_LOCK(); + + for (int i = 0; i < m_num_instances; ++i) + { + if ((m_instances[i]->object_id == p_instance->object_id) && + (m_instances[i]->instance_id == p_instance->instance_id)) + { + // Move current last entry into this index position, and trim down the length. + // If this is the last element, it cannot be accessed because the m_num_instances + // count is 0. + m_instances[i] = m_instances[m_num_instances - 1]; + --m_num_instances; + + LWM2M_MUTEX_UNLOCK(); + + return NRF_SUCCESS; + } + } + + LWM2M_MUTEX_UNLOCK(); + + return NRF_ERROR_NOT_FOUND; +} + + +uint32_t lwm2m_coap_handler_object_add(lwm2m_object_prototype_t * p_object) +{ + LWM2M_ENTRY(); + + NULL_PARAM_CHECK(p_object); + + LWM2M_MUTEX_LOCK(); + + if (m_num_objects == LWM2M_COAP_HANDLER_MAX_INSTANCES) + { + LWM2M_MUTEX_UNLOCK(); + + return NRF_ERROR_NO_MEM; + } + + m_objects[m_num_objects] = p_object; + ++m_num_objects; + + LWM2M_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t lwm2m_coap_handler_object_delete(lwm2m_object_prototype_t * p_object) +{ + LWM2M_ENTRY(); + + NULL_PARAM_CHECK(p_object); + + LWM2M_MUTEX_LOCK(); + + for (int i = 0; i < m_num_objects; ++i) + { + if ( m_objects[i]->object_id == p_object->object_id) + { + // Move current last entry into this index position, and trim down the length. + // If this is the last element, it cannot be accessed because the m_num_objects + // count is 0. + m_objects[i] = m_objects[m_num_objects - 1]; + --m_num_objects; + + LWM2M_MUTEX_UNLOCK(); + + return NRF_SUCCESS; + } + } + + LWM2M_MUTEX_UNLOCK(); + + return NRF_ERROR_NOT_FOUND; +} + + +uint32_t lwm2m_coap_handler_gen_link_format(uint8_t * p_buffer, uint16_t * p_buffer_len) +{ + + LWM2M_ENTRY(); + + NULL_PARAM_CHECK(p_buffer_len); + + LWM2M_MUTEX_LOCK(); + + uint16_t buffer_index = 0; + uint8_t * p_string_buffer; + uint16_t buffer_max_size; + + uint8_t dry_run_buffer[16]; + bool dry_run = false; + uint16_t dry_run_size = 0; + + if (p_buffer == NULL) + { + // Dry-run only, in order to calculate the size of the needed buffer. + dry_run = true; + p_string_buffer = dry_run_buffer; + buffer_max_size = sizeof(dry_run_buffer); + } + else + { + p_string_buffer = p_buffer; + buffer_max_size = *p_buffer_len; + } + + for (int i = 0; i < m_num_objects; ++i) + { + // We need more than 3 chars to write a new link + if (buffer_max_size - buffer_index <= 3) + { + LWM2M_MUTEX_UNLOCK(); + + return NRF_ERROR_NO_MEM; + } + + uint16_t curr_object = m_objects[i]->object_id; + + if (curr_object == LWM2M_NAMED_OBJECT) + { + // Skip this object as it is a named object. + continue; + } + + bool instance_present = false; + for (int j = 0; j < m_num_instances; ++j) + { + if (m_instances[j]->object_id == curr_object) + { + instance_present = true; + + buffer_index += snprintf((char *)&p_string_buffer[buffer_index], + buffer_max_size - buffer_index, + "</%u/%u>,", + m_instances[j]->object_id, + m_instances[j]->instance_id); + if (dry_run == true) + { + dry_run_size += buffer_index; + buffer_index = 0; + } + } + } + + if (!instance_present) + { + buffer_index += snprintf((char *)&p_string_buffer[buffer_index], + buffer_max_size - buffer_index, + "</%u>,", + curr_object); + if (dry_run == true) + { + dry_run_size += buffer_index; + buffer_index = 0; + } + } + } + + if (dry_run == true) + { + *p_buffer_len = dry_run_size - 1; + } + else + { + *p_buffer_len = buffer_index - 1; // Remove the last comma + } + + LWM2M_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t lwm2m_init(void) +{ + SDK_MUTEX_INIT(m_lwm2m_mutex); + + LWM2M_MUTEX_LOCK(); + + uint32_t err_code; + + err_code = internal_lwm2m_register_init(); + if (err_code != NRF_SUCCESS) + { + LWM2M_MUTEX_UNLOCK(); + return err_code; + } + + err_code = internal_lwm2m_bootstrap_init(); + if (err_code != NRF_SUCCESS) + { + LWM2M_MUTEX_UNLOCK(); + return err_code; + } + + err_code = coap_error_handler_register(coap_error_handler); + if (err_code != NRF_SUCCESS) + { + LWM2M_MUTEX_UNLOCK(); + return err_code; + } + + internal_coap_handler_init(); + + err_code = coap_request_handler_register(lwm2m_coap_handler_handle_request); + + LWM2M_MUTEX_UNLOCK(); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m.h new file mode 100644 index 0000000..fe03b4d --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m.h @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file lwm2m.h + * + * @defgroup iot_sdk_lwm2m_api LWM2M library private definitions. + * @ingroup iot_sdk_lwm2m + * @{ + * @brief LWM2M library private definitions. + */ + +#ifndef LWM2M_H__ +#define LWM2M_H__ + +#include "stdint.h" +#include "stdbool.h" +#include "coap_message.h" +#include "coap_codes.h" +#include "sdk_config.h" +#include "sdk_os.h" +#include "iot_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup iot_coap_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case the need to use an alternative architecture arises. + * @{ + */ +#define LWM2M_MUTEX_LOCK() SDK_MUTEX_LOCK(m_lwm2m_mutex) /**< Lock module using mutex */ +#define LWM2M_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_lwm2m_mutex) /**< Unlock module using mutex */ + +/** @} */ + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * LWM2M_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks. + * + * @{ + */ +#if (LWM2M_DISABLE_API_PARAM_CHECK == 0) + +/**@brief Verify NULL parameters are not passed to API by application. */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_LWM2M_ERR_BASE); \ + } +#else + +#define NULL_PARAM_CHECK(PARAM) + +#endif // LWM2M_DISABLE_API_PARAM_CHECK + +#define LWM2M_REQUEST_TYPE_BOOTSTRAP 1 +#define LWM2M_REQUEST_TYPE_REGISTER 2 +#define LWM2M_REQUEST_TYPE_UPDATE 3 +#define LWM2M_REQUEST_TYPE_DEREGISTER 4 + +#ifdef __cplusplus +} +#endif + +#endif // LWM2M_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_api.h new file mode 100644 index 0000000..444c3e5 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_api.h @@ -0,0 +1,426 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file lwm2m_api.h +* +* @defgroup iot_sdk_lwm2m_api LWM2M Application Programming Interface +* @ingroup iot_sdk_lwm2m +* @{ +* @brief Public API of Nordic's LWM2M implementation. +*/ +#ifndef LWM2M_API_H__ +#define LWM2M_API_H__ + +#include <stdint.h> + +#include "lwm2m.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@addtogroup LWM2M_opcodes Types + * @{ + * @brief LWMW2M Bootstrap type definitions. + */ + +/**@brief LWM2M remote structure type. */ +typedef coap_remote_t lwm2m_remote_t; + +/**@brief LWM2M time type. */ +typedef uint32_t lwm2m_time_t; + +/**@brief LWM2M string type. */ +typedef struct +{ + char * p_val; /**< Pointer to the value of the string data. */ + uint32_t len; /**< Length of p_val. */ +} lwm2m_string_t; + +/**@brief LWM2M opaque type. */ +typedef struct +{ + uint8_t * p_val; /**< Pointer to the value of the opaque data. */ + uint32_t len; /**< Length of p_val. */ +} lwm2m_opaque_t; + +/**@brief Application notification callback types. */ +typedef enum +{ + LWM2M_NOTIFCATION_TYPE_BOOTSTRAP, /**< Notification from a bootstrap request. */ + LWM2M_NOTIFCATION_TYPE_REGISTER, /**< Notification from a register request. */ + LWM2M_NOTIFCATION_TYPE_UPDATE, /**< Notification from a update request. */ + LWM2M_NOTIFCATION_TYPE_DEREGISTER /**< Notification from a deregister request. */ +} lwm2m_notification_type_t; + +/**@brief LWM2M server configuration type. */ +typedef struct +{ + uint32_t lifetime; /** Lifetime parameter **/ + uint64_t msisdn; /** SMS number MSISDN **/ + uint8_t lwm2m_version_major; /** LWM2M major version number **/ + uint8_t lwm2m_version_minor; /** LWM2M miner version number **/ + lwm2m_string_t binding; +} lwm2m_server_config_t; + +/**@brief LWM2M client identity types. */ +typedef enum +{ + LWM2M_CLIENT_ID_TYPE_UUID = 36, + LWM2M_CLIENT_ID_TYPE_IMEI = 15, + LWM2M_CLIENT_ID_TYPE_ESN = 8, + LWM2M_CLIENT_ID_TYPE_MEID = 14 +} lwm2m_client_identity_type_t; + +/**@brief LWM2M identity string. + * + * @details Using the string representation of UUID/OPS/OS/IMEI/ESN/MEID. + * + * @note: OPS- and OS URN are not currently supported. + */ +typedef union +{ + char uuid[36]; + char imei[15]; + char esn[8]; + char meid[14]; +} lwm2m_identity_string_t; + +/**@brief LWM2M client identity structure type. */ +typedef struct +{ + lwm2m_identity_string_t value; + lwm2m_client_identity_type_t type; +} lwm2m_client_identity_t; +/**@} */ + +/**@addtogroup LWM2M_defines Defines + * @{ + * @brief LWMW2M operation code and invalid object/instance definitions. + */ + +/** + * @warning The invalid resource and instance are not stated by the lwm2m spec as reserved and will + * cause issues if instances or resources with these IDs is added. + */ +#define LWM2M_NAMED_OBJECT 65535 /**< Flag to indicate that the object does not use Integer as object id. */ +#define LWM2M_INVALID_RESOURCE 65535 /**< Invalid Resource ID. */ +#define LWM2M_INVALID_INSTANCE 65535 /**< Invalid Instance ID. */ + +#define LWM2M_OPERATION_CODE_NONE 0x00 /**< Bit mask for LWM2M no operation. */ +#define LWM2M_OPERATION_CODE_READ 0x01 /**< Bit mask for LWM2M read operation. */ +#define LWM2M_OPERATION_CODE_WRITE 0x02 /**< Bit mask for LWM2M write operation. */ +#define LWM2M_OPERATION_CODE_EXECUTE 0x04 /**< Bit mask for LWM2M execute operation. */ +#define LWM2M_OPERATION_CODE_DELETE 0x08 /**< Bit mask for LWM2M delete operation. */ +#define LWM2M_OPERATION_CODE_CREATE 0x10 /**< Bit mask for LWM2M create operation. */ +#define LWM2M_OPERATION_CODE_DISCOVER 0x20 /**< Bit mask for LWM2M discover operation. */ +#define LWM2M_OPERATION_CODE_OBSERVE 0x40 /**< Bit mask for LWM2M observe operation. */ +/**@} */ + +/**@cond */ +// Forward declare structs. +typedef struct lwm2m_object_prototype_t lwm2m_object_prototype_t; +typedef struct lwm2m_instance_prototype_t lwm2m_instance_prototype_t; +/**@endcond */ + +/**@brief Callback function upon requests on a given LWM2M resource instance. + * + * @details Will be called when the request is for an instance Ex. /0/1. + * + * If no instance could be located the object callback will be called. + * The instance_id corresponds to the one in the URI-patch in the CoAP request and may be used to + * create a new instance. If the value of resource_id is set to LWM2M_INVALID_RESOURCE the callback + * should treated it as a call to the instance instead of a resource inside of the instance. + * + * If a resource has been found p_instance pointer will be set, else it will be NULL. + * + * @param[in] p_instance Pointer to the located resource if it already exists. + * @param[in] resource_id Id of the resource requested. + * @param[in] op_code Opcode of the request. Values of the opcodes are defined + * in \ref LWM2M_opcodes. + * @param[in] p_request Pointer to the CoAP request message. + * + * @retval NRF_SUCCESS Will always return success. + */ +typedef uint32_t (*lwm2m_instance_callback_t)(lwm2m_instance_prototype_t * p_instance, + uint16_t resource_id, + uint8_t op_code, + coap_message_t * p_request); + +/**@brief Callback function upon request on a given LWM2M object or instance create. + * + * @details Will be called when the request is for an object Ex: /0 or /0/1 an instance and the + * op_code is CREATE. Depending on the CoAP request code the user might create an instance + * or just return the tlv of current instances. If the value of instance_id is set to + * LWM2M_INVALID_INSTANCE the callback should treated it as a call to the instance instead + * of an instance of the object. + * + * @param[in] p_object Pointer to the located object. + * @param[in] instance_id Id of the instance requested. + * @param[in] op_code Opcode of the request. Values of the opcodes are defined + * in \ref LWM2M_opcodes. + * @param[in] p_request Pointer to the CoAP request message. + * + * @retval NRF_SUCCESS Will always return success. + */ +typedef uint32_t (*lwm2m_object_callback_t)(lwm2m_object_prototype_t * p_object, + uint16_t instance_id, + uint8_t op_code, + coap_message_t * p_request); + +/**@brief LWM2M object prototype structure. + * + * @details Each instance will have this structure in the front of its instance structure. + * The object is used to have a common way of looking up its object id and callback + * structure for each of the inherited. As there is no instance of the objects themselves, + * the prototype is used as a meta object in order to have a common set of functions + * for all instances of a object kind. + */ +struct lwm2m_object_prototype_t +{ + uint16_t object_id; /**< Identifies the object. */ + lwm2m_object_callback_t callback; /**< Called when for request to /0 (object) and /0/1 if instance 1 is not found. */ + char * p_alias_name; /**< Alternative name of the resource, used when LWM2M_NAMED_OBJECT is set. */ +}; + +/**@brief LWM2M instance structure. + * + * @details Prototype for the instance object, this enables us to search through the instances + * without knowing the type. + */ +struct lwm2m_instance_prototype_t +{ + uint16_t object_id; /**< Identifies what object this instance belongs to. */ + uint16_t instance_id; /**< Used to identify the instance. */ + uint16_t num_resources; /**< Number of resources MUST equal number of members in the lwm2m instance, sizeof resource_access and sizeof resource_ids. */ + uint8_t operations_offset; /**< Internal use. */ + uint8_t resource_ids_offset; /**< Internal use. */ + lwm2m_instance_callback_t callback; /**< Called when an operation is done on this instance. */ +}; + +/**@brief Callback interface from the enabler interface (bootstrap/register) to the application. + * + * @warning This is an interface function. MUST BE IMPLEMENTED BY APPLICATION. + * + * @param[in] type Notification type. The types are defined in \ref lwm2m_notification_type_t. + * @param[in] p_remote remote that answered the request + * @param[in] coap_code coap op code from the response + * + * @retval NRF_SUCCESS If the client application handled the notification successfully. + */ +uint32_t lwm2m_notification(lwm2m_notification_type_t type, + lwm2m_remote_t * p_remote, + uint8_t coap_code); + +/**@brief CoAP Request handler for the root of the object/instance/resource hierarchy. + * + * @details The function is called when a request is for the lwm2m root (ie no object instance + * or resource). + * + * @warning This is an interface function. MUST BE IMPLEMENTED BY APPLICATION. + * + * @param[in] op_code LWM2M operation code. + * @param[in] p_request Pointer to CoAP request message. + * + * @retval NRF_SUCCESS If the handler processed the request successfully. + */ +uint32_t lwm2m_coap_handler_root(uint8_t op_code, coap_message_t * p_request); + +/**@brief Initialize LWM2M library. + * + * @retval NRF_SUCCESS If initialization was successful. + */ +uint32_t lwm2m_init(void); + +/**@brief Send bootstrap request to a remote bootstrap server. + * + * @details Sends a bootstrap request with specified ID to the specified remote, calls the + * lwm2m_notification with answer from the bootstrap server. + * + * @param[in] p_remote Pointer to the structure holding connection information of the remote + * LWM2M bootstrap server. + * @param[in] p_id Pointer to the structure holding the Id of the client. + * @param[in] local_port Port number of the local port to be used to send the bootstrap request. + * + * @retval NRF_SUCCESS If bootstrap request to the LWM2M bootstrap server was sent successfully. + * @retval NRF_ERROR_NULL If one of the parameters was a NULL pointer. + */ +uint32_t lwm2m_bootstrap(lwm2m_remote_t * p_remote, + lwm2m_client_identity_t * p_id, + uint16_t local_port); + +/**@brief Register with a remote LWM2M server. + * + * @param[in] p_remote Pointer to the structure holding connection information + * of the remote LWM2M server. + * @param[in] p_id Pointer to the structure holding the Id of the client. + * @param[in] p_config Registration parameters. + * @param[in] local_port Port number of the local port to be used to send the + * register request. + * @param[in] p_link_format_string Pointer to a link format encoded string to send in the + * register request. + * @param[in] link_format_len Length of the link format string provided. + * + * @retval NRF_SUCCESS If registration request to the LWM2M server was sent out successfully. + */ +uint32_t lwm2m_register(lwm2m_remote_t * p_remote, + lwm2m_client_identity_t * p_id , + lwm2m_server_config_t * p_config, + uint16_t local_port, + uint8_t * p_link_format_string, + uint16_t link_format_len); + +/**@brief Update a registration with a remote server. + * + * @param[in] p_remote Pointer to the structure holding connection information of the remote + * LWM2M server. + * @param[in] p_config Registration parameters. + * @param[in] local_port Port number of the local port to be used to send the update request. + * + * @retval NRF_SUCCESS If update request to the LWM2M server was sent out successfully. + */ +uint32_t lwm2m_update(lwm2m_remote_t * p_remote, + lwm2m_server_config_t * p_config, + uint16_t local_port); + +/**@brief Deregister from a remote server. + * + * @param[in] p_remote Pointer to the structure holding connection information of the remote + * LWM2M server. + * @param[in] local_port Port number of the local port to be used to send the deregister request. + * + * @retval NRF_SUCCESS If deregister request to the LWM2M server was sent out successfully. + */ +uint32_t lwm2m_deregister(lwm2m_remote_t * p_remote, uint16_t local_port); + +/**@brief Add an instance to coap_handler in order to match requests to the given instance. + * + * @details Add a new LWM2M instance to the coap_handler. The application MUST initialize + * and allocate the additional data in the struct. + * + * @param[in] p_instance Pointer to the instance to add. + * + * @retval NRF_SUCCESS If registration was successful. + * @retval NRF_ERROR_NO_MEM If the module was not able to add the instance. Verify that + * the LWM2M_COAP_HANDLER_MAX_INSTANCES setting in sdk_config.h + * has a correct value. + */ +uint32_t lwm2m_coap_handler_instance_add(lwm2m_instance_prototype_t * p_instance); + +/**@brief Delete an instance from coap_handler in order to stop matching requests to the given + * instance. + * + * @param[in] p_instance Pointer to the instance to delete. + * + * @retval NRF_SUCCESS If unregistration was a success. + * @retval NRF_ERROR_NOT_FOUND If the given instance was not located. + */ +uint32_t lwm2m_coap_handler_instance_delete(lwm2m_instance_prototype_t * p_instance); + +/**@brief Add an object to coap_handler in order to match requests to the given object. + * + * @details Add a new LWM2M object to the coap_handler. The application MUST initialize + * and allocate the additional data in the struct. + * + * @param[in] p_object Pointer to the object to add. + * + * @retval NRF_SUCCESS If registration was successful. + * @retval NRF_ERROR_NO_MEM If the module was not able to add the object. Verify that + * the LWM2M_COAP_HANDLER_MAX_OBJECTS setting in sdk_config.h + * has a correct value. + */ +uint32_t lwm2m_coap_handler_object_add(lwm2m_object_prototype_t * p_object); + +/**@brief Delete an object from coap_handler in order to stop matching requests to the given + * object. + * + * @param[in] p_object Pointer to the object to delete. + * + * @retval NRF_SUCCESS If unregistration was a success. + * @retval NRF_ERROR_NOT_FOUND If the given object was not located. + */ +uint32_t lwm2m_coap_handler_object_delete(lwm2m_object_prototype_t * p_object); + +/**@brief Generate link format string based on registered objects and instances. + * + * @note For generation of links to work properly it is required that objects is added + * before instances. + * + * @param[inout] p_buffer Pointer to a buffer to fill with link format encoded string. If + * a NULL pointer is provided the function will dry-run the function + * in order to calculate how much memory that is needed for the link + * format string. + * @param[inout] p_buffer_len As input used to indicate the length of the buffer. It will return the + * used amount of buffer length by reference in response. If NULL pointer + * is provided for p_buffer, the value by reference output will be the number + * of bytes needed to generate the link format string. + * + * @retval NRF_SUCCESS If generation of link format string was successful. + * @retval NRF_ERROR_NO_MEM If the provided memory was not large enough. + */ +uint32_t lwm2m_coap_handler_gen_link_format(uint8_t * p_buffer, uint16_t * p_buffer_len); + +/**@brief Send CoAP 2.05 Content response with the payload provided. + * + * @param[in] p_payload Pointer to the payload to send. Must not be NULL. + * @param[in] payload_len Size of the payload. + * @param[in] p_request Original CoAP request. Must not be NULL. + * + * @retval NRF_SUCCESS If the response was sent out successfully. + */ +uint32_t lwm2m_respond_with_payload(uint8_t * p_payload, + uint16_t payload_len, + coap_message_t * p_request); + +/**@brief Send CoAP response with a given CoAP message code. + * + * @param [in] code CoAP response code to send. + * @param [in] p_request Original CoAP request. Must not be NULL. + * + * @retval NRF_SUCCESS If the response was sent out successfully. + */ +uint32_t lwm2m_respond_with_code(coap_msg_code_t code, coap_message_t * p_request); + +#ifdef __cplusplus +} +#endif + +#endif // LWM2M_API_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_bootstrap.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_bootstrap.c new file mode 100644 index 0000000..7724e10 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_bootstrap.c @@ -0,0 +1,203 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include "lwm2m_api.h" +#include "lwm2m_bootstrap.h" +#include "lwm2m.h" +#include "coap_api.h" +#include "coap_message.h" +#include "coap_codes.h" +#include "sdk_config.h" +#include "app_util.h" + +#if LWM2M_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME lwm2m + +#define NRF_LOG_LEVEL LWM2M_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR LWM2M_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR LWM2M_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" + +#define LWM2M_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define LWM2M_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define LWM2M_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define LWM2M_ENTRY() LWM2M_TRC(">> %s", __func__) +#define LWM2M_EXIT() LWM2M_TRC("<< %s", __func__) + +#else // LWM2M_CONFIG_LOG_ENABLED + +#define LWM2M_TRC(...) /**< Disables traces. */ +#define LWM2M_DUMP(...) /**< Disables dumping of octet streams. */ +#define LWM2M_ERR(...) /**< Disables error logs. */ + +#define LWM2M_ENTRY(...) +#define LWM2M_EXIT(...) + +#endif // LWM2M_CONFIG_LOG_ENABLED + +#define LWM2M_BOOTSTRAP_URI_PATH "bs" + +#define TOKEN_START 0x012A + +static uint16_t m_token = TOKEN_START; + +static uint32_t internal_message_new(coap_message_t ** pp_msg, + coap_msg_code_t code, + coap_response_callback_t callback, + uint16_t local_port) +{ + uint32_t err_code; + coap_message_conf_t conf; + memset (&conf, 0, sizeof(coap_message_conf_t)); + + conf.type = COAP_TYPE_CON; + conf.code = code; + conf.response_callback = callback; + conf.port.port_number = local_port; + + conf.token_len = uint16_encode(m_token, conf.token); + + m_token++; + + err_code = coap_message_new(pp_msg, &conf); + + return err_code; +} + + +/**@brief Function to be used as callback function upon a bootstrap request. */ +static void lwm2m_bootstrap_cb(uint32_t status, void * p_arg, coap_message_t * p_message) +{ + LWM2M_TRC("[Bootstrap]: lwm2m_bootstrap_cb, status: %ul, coap code: %u", + status, + p_message->header.code); + + (void)lwm2m_notification(LWM2M_NOTIFCATION_TYPE_BOOTSTRAP, + &p_message->remote, + p_message->header.code); +} + + +uint32_t internal_lwm2m_bootstrap_init(void) +{ + m_token = TOKEN_START; + return NRF_SUCCESS; +} + + +uint32_t lwm2m_bootstrap(lwm2m_remote_t * p_remote, lwm2m_client_identity_t * p_id, uint16_t local_port) +{ + LWM2M_ENTRY(); + + NULL_PARAM_CHECK(p_remote); + NULL_PARAM_CHECK(p_id); + + LWM2M_MUTEX_LOCK(); + + uint32_t err_code; + coap_message_t * p_msg; + + lwm2m_string_t endpoint; + + endpoint.p_val = LWM2M_BOOTSTRAP_URI_PATH; + endpoint.len = 2; + + err_code = internal_message_new(&p_msg, COAP_CODE_POST, lwm2m_bootstrap_cb, local_port); + if (err_code != NRF_SUCCESS) + { + LWM2M_MUTEX_UNLOCK(); + return err_code; + } + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_remote_addr_set(p_msg, p_remote); + } + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_opt_str_add(p_msg, + COAP_OPT_URI_PATH, + (uint8_t *)endpoint.p_val, + endpoint.len); // end_point length is always 2 + } + + if (err_code == NRF_SUCCESS) + { + char buffer[40]; + buffer[0] = 'e'; + buffer[1] = 'p'; + buffer[2] = '='; + memcpy(buffer + 3, &p_id->value, p_id->type); + + err_code = coap_message_opt_str_add(p_msg, + COAP_OPT_URI_QUERY, + (uint8_t *)buffer, + p_id->type + 3); + } + + if (err_code == NRF_SUCCESS) + { + uint32_t msg_handle; + err_code = coap_message_send(&msg_handle, p_msg); + } + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_delete(p_msg); + } + else + { + // If we have hit an error try to clean up. + // Return the original error code. + (void)coap_message_delete(p_msg); + } + + LWM2M_EXIT(); + + LWM2M_MUTEX_UNLOCK(); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_bootstrap.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_bootstrap.h new file mode 100644 index 0000000..68bdf6b --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_bootstrap.h @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file lwm2m_bootstrap.h + * + * @defgroup iot_sdk_lwm2m_bootstrap_api LWM2M bootstrap API interface + * @ingroup iot_sdk_lwm2m + * @{ + * @brief Bootstrap API interface for the LWM2M protocol. + */ + +#ifndef LWM2M_BOOTSTRAP_H__ +#define LWM2M_BOOTSTRAP_H__ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Initialize the LWM2M register module. + * + * @details Calling this function will set the module in default state. + */ +uint32_t internal_lwm2m_bootstrap_init(void); + +#ifdef __cplusplus +} +#endif + +#endif // LWM2M_BOOTSTRAP_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_coap_util.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_coap_util.c new file mode 100644 index 0000000..bf7bece --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_coap_util.c @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <string.h> +#include "coap_api.h" +#include "coap_message.h" +#include "coap_codes.h" +#include "lwm2m.h" + + +uint32_t lwm2m_respond_with_code(coap_msg_code_t code, coap_message_t * p_request) +{ + NULL_PARAM_CHECK(p_request); + + // Application helper function, no need for mutex. + coap_message_conf_t response_config; + memset (&response_config, 0, sizeof(coap_message_conf_t)); + + if (p_request->header.type == COAP_TYPE_NON) + { + response_config.type = COAP_TYPE_NON; + } + else if (p_request->header.type == COAP_TYPE_CON) + { + response_config.type = COAP_TYPE_ACK; + } + + // PIGGY BACKED RESPONSE + response_config.code = code; + response_config.id = p_request->header.id; + response_config.port.port_number = p_request->port.port_number; + + // Copy token. + memcpy(&response_config.token[0], &p_request->token[0], p_request->header.token_len); + // Copy token length. + response_config.token_len = p_request->header.token_len; + + coap_message_t * p_response; + uint32_t err_code = coap_message_new(&p_response, &response_config); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + err_code = coap_message_remote_addr_set(p_response, &p_request->remote); + if (err_code != NRF_SUCCESS) + { + (void)coap_message_delete(p_response); + return err_code; + } + + memcpy(&p_response->remote, &p_request->remote, sizeof(coap_remote_t)); + + uint32_t msg_handle; + err_code = coap_message_send(&msg_handle, p_response); + if (err_code != NRF_SUCCESS) + { + (void)coap_message_delete(p_response); + return err_code; + } + + err_code = coap_message_delete(p_response); + + return err_code; +} + + +uint32_t lwm2m_respond_with_payload(uint8_t * p_payload, uint16_t payload_len, coap_message_t * p_request) +{ + NULL_PARAM_CHECK(p_request); + NULL_PARAM_CHECK(p_payload); + + // Application helper function, no need for mutex. + coap_message_conf_t response_config; + memset (&response_config, 0, sizeof(coap_message_conf_t)); + + if (p_request->header.type == COAP_TYPE_NON) + { + response_config.type = COAP_TYPE_NON; + } + else if (p_request->header.type == COAP_TYPE_CON) + { + response_config.type = COAP_TYPE_ACK; + } + + // PIGGY BACKED RESPONSE + response_config.code = COAP_CODE_205_CONTENT; + response_config.id = p_request->header.id; + response_config.port.port_number = p_request->port.port_number; + + // Copy token. + memcpy(&response_config.token[0], &p_request->token[0], p_request->header.token_len); + // Copy token length. + response_config.token_len = p_request->header.token_len; + + coap_message_t * p_response; + uint32_t err_code = coap_message_new(&p_response, &response_config); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + err_code = coap_message_payload_set(p_response, p_payload, payload_len); + if (err_code != NRF_SUCCESS) + { + (void)coap_message_delete(p_response); + return err_code; + } + + err_code = coap_message_remote_addr_set(p_response, &p_request->remote); + if (err_code != NRF_SUCCESS) + { + (void)coap_message_delete(p_response); + return err_code; + } + + memcpy(&p_response->remote, &p_request->remote, sizeof(coap_remote_t)); + + uint32_t msg_handle; + err_code = coap_message_send(&msg_handle, p_response); + if (err_code != NRF_SUCCESS) + { + (void)coap_message_delete(p_response); + return err_code; + } + + err_code = coap_message_delete(p_response); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects.c new file mode 100644 index 0000000..e61a40e --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects.c @@ -0,0 +1,347 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <string.h> +#include <stddef.h> + +#include "lwm2m_objects.h" +#include "lwm2m.h" + +//lint -e516 -save // Symbol '__INTADDR__()' has arg. type conflict +#define LWM2M_INSTANCE_OFFSET_SET(instance, type) \ + instance->proto.operations_offset = offsetof(type, operations); \ + instance->proto.resource_ids_offset = offsetof(type, resource_ids); +//lint -restore + + +void lwm2m_instance_security_init(lwm2m_security_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, lwm2m_security_t); + + p_instance->proto.object_id = LWM2M_OBJ_SECURITY; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((lwm2m_security_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[1] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[2] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[3] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[4] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[5] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[6] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[7] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[8] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[9] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[10] = LWM2M_OPERATION_CODE_NONE; + p_instance->operations[11] = LWM2M_OPERATION_CODE_NONE; + + // Set resource IDs. + p_instance->resource_ids[0] = LWM2M_SECURITY_SERVER_URI; + p_instance->resource_ids[1] = LWM2M_SECURITY_BOOTSTRAP_SERVER; + p_instance->resource_ids[2] = LWM2M_SECURITY_SECURITY_MODE; + p_instance->resource_ids[3] = LWM2M_SECURITY_PUBLIC_KEY; + p_instance->resource_ids[4] = LWM2M_SECURITY_SERVER_PUBLIC_KEY; + p_instance->resource_ids[5] = LWM2M_SECURITY_SECRET_KEY; + p_instance->resource_ids[6] = LWM2M_SECURITY_SMS_SECURITY_MODE; + p_instance->resource_ids[7] = LWM2M_SECURITY_SMS_BINDING_KEY_PARAM; + p_instance->resource_ids[8] = LWM2M_SECURITY_SMS_BINDING_SECRET_KEY; + p_instance->resource_ids[9] = LWM2M_SECURITY_SERVER_SMS_NUMBER; + p_instance->resource_ids[10] = LWM2M_SECURITY_SHORT_SERVER_ID; + p_instance->resource_ids[11] = LWM2M_SECURITY_CLIENT_HOLD_OFF_TIME; +} + + +void lwm2m_instance_server_init(lwm2m_server_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, lwm2m_server_t); + + p_instance->proto.object_id = LWM2M_OBJ_SERVER; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((lwm2m_server_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[2] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[3] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[4] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[5] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[6] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[7] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[8] = LWM2M_OPERATION_CODE_EXECUTE; + + // Set resource IDs. + p_instance->resource_ids[0] = LWM2M_SERVER_SHORT_SERVER_ID; + p_instance->resource_ids[1] = LWM2M_SERVER_LIFETIME; + p_instance->resource_ids[2] = LWM2M_SERVER_DEFAULT_MIN_PERIOD; + p_instance->resource_ids[3] = LWM2M_SERVER_DEFAULT_MAX_PERIOD; + p_instance->resource_ids[4] = LWM2M_SERVER_DISABLE; + p_instance->resource_ids[5] = LWM2M_SERVER_DISABLE_TIMEOUT; + p_instance->resource_ids[6] = LWM2M_SERVER_NOTIFY_WHEN_DISABLED; + p_instance->resource_ids[7] = LWM2M_SERVER_BINDING; + p_instance->resource_ids[8] = LWM2M_SERVER_REGISTRATION_UPDATE_TRIGGER; +} + + +void lwm2m_instance_firmware_init(lwm2m_firmware_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, lwm2m_firmware_t); + + p_instance->proto.object_id = LWM2M_OBJ_FIRMWARE; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((lwm2m_firmware_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_WRITE; + p_instance->operations[1] = LWM2M_OPERATION_CODE_WRITE; + p_instance->operations[2] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + + // Set resource IDs. + p_instance->resource_ids[0] = LWM2M_FIRMWARE_PACKAGE; + p_instance->resource_ids[1] = LWM2M_FIRMWARE_PACKAGE_URI; + p_instance->resource_ids[2] = LWM2M_FIRMWARE_UPDATE; + p_instance->resource_ids[3] = LWM2M_FIRMWARE_STATE; + p_instance->resource_ids[4] = LWM2M_FIRMWARE_UPDATE_SUPPORTED_OBJECTS; + p_instance->resource_ids[5] = LWM2M_FIRMWARE_UPDATE_RESULT; +} + + +void lwm2m_instance_acl_init(lwm2m_acl_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, lwm2m_acl_t); + + p_instance->proto.object_id = LWM2M_OBJ_ACL; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((lwm2m_acl_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[3] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + + // Set resource IDs. + p_instance->resource_ids[0] = LWM2M_ACL_OBJECT_ID; + p_instance->resource_ids[1] = LWM2M_ACL_INSTANCE_ID; + p_instance->resource_ids[2] = LWM2M_ACL_ACL; + p_instance->resource_ids[3] = LWM2M_ACL_CONTROL_OWNER; +} + + +void lwm2m_instance_connectivity_monitoring_init(lwm2m_connectivity_monitoring_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, lwm2m_connectivity_monitoring_t); + + p_instance->proto.object_id = LWM2M_OBJ_CONN_MON; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((lwm2m_connectivity_monitoring_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[6] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[7] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[8] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[9] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[10] = LWM2M_OPERATION_CODE_READ; + + // Set resource IDs. + p_instance->resource_ids[0] = LWM2M_CONN_MON_NETWORK_BEARER; + p_instance->resource_ids[1] = LWM2M_CONN_MON_AVAILABLE_NETWORK_BEARER; + p_instance->resource_ids[2] = LWM2M_CONN_MON_RADIO_SIGNAL_STRENGHT; + p_instance->resource_ids[3] = LWM2M_CONN_MON_LINK_QUALITY; + p_instance->resource_ids[4] = LWM2M_CONN_MON_IP_ADDRESSES; + p_instance->resource_ids[5] = LWM2M_CONN_MON_ROUTER_IP_ADRESSES; + p_instance->resource_ids[6] = LWM2M_CONN_MON_LINK_UTILIZATION; + p_instance->resource_ids[7] = LWM2M_CONN_MON_APN; + p_instance->resource_ids[8] = LWM2M_CONN_MON_CELL_ID; + p_instance->resource_ids[9] = LWM2M_CONN_MON_SMNC; + p_instance->resource_ids[10] = LWM2M_CONN_MON_SMCC; +} + + +void lwm2m_instance_connectivity_statistics_init(lwm2m_connectivity_statistics_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, lwm2m_connectivity_statistics_t); + + p_instance->proto.object_id = LWM2M_OBJ_CONN_STAT; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((lwm2m_connectivity_statistics_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[6] = LWM2M_OPERATION_CODE_EXECUTE; + + // Set resource IDs. + p_instance->resource_ids[0] = LWM2M_CONN_STAT_SMS_TX_COUNTER; + p_instance->resource_ids[1] = LWM2M_CONN_STAT_SMS_RX_COUNTER; + p_instance->resource_ids[2] = LWM2M_CONN_STAT_TX_DATA; + p_instance->resource_ids[3] = LWM2M_CONN_STAT_RX_DATA; + p_instance->resource_ids[4] = LWM2M_CONN_STAT_MAX_MSG_SIZE; + p_instance->resource_ids[5] = LWM2M_CONN_STAT_AVG_MSG_SIZE; + p_instance->resource_ids[6] = LWM2M_CONN_STAT_START_RESET; +} + + +void lwm2m_instance_device_init(lwm2m_device_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, lwm2m_device_t); + + p_instance->proto.object_id = LWM2M_OBJ_DEVICE; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((lwm2m_device_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[5] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[6] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[7] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[8] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[9] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[10] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[11] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[12] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[13] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[14] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[15] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + p_instance->operations[16] = LWM2M_OPERATION_CODE_READ; + + // Set resource IDs. + p_instance->resource_ids[0] = LWM2M_DEVICE_MANUFACTURER; + p_instance->resource_ids[1] = LWM2M_DEVICE_MODEL_NUMBER; + p_instance->resource_ids[2] = LWM2M_DEVICE_SERIAL_NUMBER; + p_instance->resource_ids[3] = LWM2M_DEVICE_FIRMWARE_VERSION; + p_instance->resource_ids[4] = LWM2M_DEVICE_REBOOT; + p_instance->resource_ids[5] = LWM2M_DEVICE_FACTORY_RESET; + p_instance->resource_ids[6] = LWM2M_DEVICE_AVAILABLE_POWER_SOURCES; + p_instance->resource_ids[7] = LWM2M_DEVICE_POWER_SOURCE_VOLTAGE; + p_instance->resource_ids[8] = LWM2M_DEVICE_POWER_SOURCE_CURRENT; + p_instance->resource_ids[9] = LWM2M_DEVICE_BATTERY_LEVEL; + p_instance->resource_ids[10] = LWM2M_DEVICE_MEMORY_FREE; + p_instance->resource_ids[11] = LWM2M_DEVICE_ERROR_CODE; + p_instance->resource_ids[12] = LWM2M_DEVICE_RESET_ERROR_CODE; + p_instance->resource_ids[13] = LWM2M_DEVICE_CURRENT_TIME; + p_instance->resource_ids[14] = LWM2M_DEVICE_UTC_OFFSET; + p_instance->resource_ids[15] = LWM2M_DEVICE_TIMEZONE; + p_instance->resource_ids[16] = LWM2M_DEVICE_SUPPORTED_BINDINGS; +} + + +void lwm2m_instance_location_init(lwm2m_location_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, lwm2m_location_t); + + p_instance->proto.object_id = LWM2M_OBJ_LOCATION; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((lwm2m_location_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[3] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[4] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + + // Set resource IDs. + p_instance->resource_ids[0] = LWM2M_LOCATION_ALTITUDE; + p_instance->resource_ids[1] = LWM2M_LOCATION_LONGITUDE; + p_instance->resource_ids[2] = LWM2M_LOCATION_ALTITUDE; + p_instance->resource_ids[3] = LWM2M_LOCATION_UNCERTAINTY; + p_instance->resource_ids[4] = LWM2M_LOCATION_VELOCITY; + p_instance->resource_ids[5] = LWM2M_LOCATION_TIMESTAMP; +} + + +void lwm2m_instance_software_update_init(lwm2m_software_update_t * p_instance) +{ + // Set prototype variables. + LWM2M_INSTANCE_OFFSET_SET(p_instance, lwm2m_software_update_t); + + p_instance->proto.object_id = LWM2M_OBJ_SOFTWARE_UPDATE; + p_instance->proto.instance_id = 0; + p_instance->proto.num_resources = sizeof(((lwm2m_software_update_t *)0)->operations); + + // Set access types. + p_instance->operations[0] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[1] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[2] = LWM2M_OPERATION_CODE_WRITE; + p_instance->operations[3] = LWM2M_OPERATION_CODE_WRITE; + p_instance->operations[4] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[5] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[6] = LWM2M_OPERATION_CODE_EXECUTE; + p_instance->operations[7] = LWM2M_OPERATION_CODE_READ; + p_instance->operations[8] = (LWM2M_OPERATION_CODE_READ | LWM2M_OPERATION_CODE_WRITE); + + // Set resource IDs. + p_instance->resource_ids[0] = LWM2M_SW_UPDATE_PKG_NAME; + p_instance->resource_ids[1] = LWM2M_SW_UPDATE_PKG_VERSION; + p_instance->resource_ids[2] = LWM2M_SW_UPDATE_PACKAGE; + p_instance->resource_ids[3] = LWM2M_SW_UPDATE_PACKAGE_URI; + p_instance->resource_ids[4] = LWM2M_SW_UPDATE_INSTALL; + p_instance->resource_ids[5] = LWM2M_SW_UPDATE_CHECKPOINT; + p_instance->resource_ids[6] = LWM2M_SW_UPDATE_UNINSTALL; + p_instance->resource_ids[7] = LWM2M_SW_UPDATE_UPDATE_STATE; + p_instance->resource_ids[8] = LWM2M_SW_UPDATE_SUPPORTED_OBJECTS; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects.h new file mode 100644 index 0000000..c18917e --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects.h @@ -0,0 +1,443 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file lwm2m_objects.h + * + * @defgroup iot_sdk_lwm2m_objects OMA LWM2M objects definitions and types + * @ingroup iot_sdk_lwm2m + * @{ + * @brief OMA LWM2M objects definitions and types. + * + * @note The definitions used in this module are from the OMA LWM2M + * "Lightweight Machine to Machine Technical Specification - OMA_TS-LightweightM2M-V1_0-20131210-C". + * The specification could be found at http://openmobilealliance.org/. + */ + +#ifndef LWM2M_OBJECTS_H__ +#define LWM2M_OBJECTS_H__ + +#include <stdint.h> +#include <stdbool.h> +#include "lwm2m_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* @brief LWM2M Enabler Object IDs Appendix E */ +#define LWM2M_OBJ_SECURITY 0 +#define LWM2M_OBJ_SERVER 1 +#define LWM2M_OBJ_ACL 2 +#define LWM2M_OBJ_DEVICE 3 +#define LWM2M_OBJ_CONN_MON 4 +#define LWM2M_OBJ_FIRMWARE 5 +#define LWM2M_OBJ_LOCATION 6 +#define LWM2M_OBJ_CONN_STAT 7 + +/* @brief LWM2M Registry Objects */ +#define LWM2M_OBJ_SOFTWARE_UPDATE 9 + +/* LWM2M Security Resource IDs Appendix E.1 */ +#define LWM2M_SECURITY_SERVER_URI 0 +#define LWM2M_SECURITY_BOOTSTRAP_SERVER 1 +#define LWM2M_SECURITY_SECURITY_MODE 2 +#define LWM2M_SECURITY_PUBLIC_KEY 3 +#define LWM2M_SECURITY_SERVER_PUBLIC_KEY 4 +#define LWM2M_SECURITY_SECRET_KEY 5 +#define LWM2M_SECURITY_SMS_SECURITY_MODE 6 +#define LWM2M_SECURITY_SMS_BINDING_KEY_PARAM 7 +#define LWM2M_SECURITY_SMS_BINDING_SECRET_KEY 8 +#define LWM2M_SECURITY_SERVER_SMS_NUMBER 9 +#define LWM2M_SECURITY_SHORT_SERVER_ID 10 +#define LWM2M_SECURITY_CLIENT_HOLD_OFF_TIME 11 + + +/* LWM2M Server Resources Appendix E.2 */ +#define LWM2M_SERVER_SHORT_SERVER_ID 0 +#define LWM2M_SERVER_LIFETIME 1 +#define LWM2M_SERVER_DEFAULT_MIN_PERIOD 2 +#define LWM2M_SERVER_DEFAULT_MAX_PERIOD 3 +#define LWM2M_SERVER_DISABLE 4 +#define LWM2M_SERVER_DISABLE_TIMEOUT 5 +#define LWM2M_SERVER_NOTIFY_WHEN_DISABLED 6 +#define LWM2M_SERVER_BINDING 7 +#define LWM2M_SERVER_REGISTRATION_UPDATE_TRIGGER 8 + + +/* LWM2M Firmware update Resources Appendix E.6 */ +#define LWM2M_FIRMWARE_PACKAGE 0 +#define LWM2M_FIRMWARE_PACKAGE_URI 1 +#define LWM2M_FIRMWARE_UPDATE 2 +#define LWM2M_FIRMWARE_STATE 3 +#define LWM2M_FIRMWARE_UPDATE_SUPPORTED_OBJECTS 4 +#define LWM2M_FIRMWARE_UPDATE_RESULT 5 + +#define LWM2M_FIRMWARE_STATE_IDLE 1 +#define LWM2M_FIRMWARE_STATE_DOWNLOADING 2 +#define LWM2M_FIRMWARE_STATE_DOWNLOADED 3 + +#define LWM2M_FIRMWARE_UPDATE_RESULT_DEFAULT 0 +#define LWM2M_FIRMWARE_UPDATE_RESULT_SUCCESS 1 +#define LWM2M_FIRMWARE_UPDATE_RESULT_ERROR_STORAGE 2 +#define LWM2M_FIRMWARE_UPDATE_RESULT_ERROR_MEMORY 3 +#define LWM2M_FIRMWARE_UPDATE_RESULT_ERROR_CONN_LOST 4 +#define LWM2M_FIRMWARE_UPDATE_RESULT_ERROR_CRC 5 +#define LWM2M_FIRMWARE_UPDATE_RESULT_ERROR_UNSUPPORTED 6 +#define LWM2M_FIRMWARE_UPDATE_RESULT_ERROR_INVALID_URI 7 + + +/* LWM2M ACL Resources */ +#define LWM2M_ACL_OBJECT_ID 0 +#define LWM2M_ACL_INSTANCE_ID 1 +#define LWM2M_ACL_ACL 2 +#define LWM2M_ACL_CONTROL_OWNER 3 + +/* LWM2M Connectivity Monitoring Resources */ +#define LWM2M_CONN_MON_NETWORK_BEARER 0 +#define LWM2M_CONN_MON_AVAILABLE_NETWORK_BEARER 1 +#define LWM2M_CONN_MON_RADIO_SIGNAL_STRENGHT 2 +#define LWM2M_CONN_MON_LINK_QUALITY 3 +#define LWM2M_CONN_MON_IP_ADDRESSES 4 +#define LWM2M_CONN_MON_ROUTER_IP_ADRESSES 5 +#define LWM2M_CONN_MON_LINK_UTILIZATION 6 +#define LWM2M_CONN_MON_APN 7 +#define LWM2M_CONN_MON_CELL_ID 8 +#define LWM2M_CONN_MON_SMNC 9 +#define LWM2M_CONN_MON_SMCC 10 + +/* LWM2M Connectivity Statistics */ +#define LWM2M_CONN_STAT_SMS_TX_COUNTER 0 +#define LWM2M_CONN_STAT_SMS_RX_COUNTER 1 +#define LWM2M_CONN_STAT_TX_DATA 2 +#define LWM2M_CONN_STAT_RX_DATA 3 +#define LWM2M_CONN_STAT_MAX_MSG_SIZE 4 +#define LWM2M_CONN_STAT_AVG_MSG_SIZE 5 +#define LWM2M_CONN_STAT_START_RESET 6 + +/* LWM2M Device */ +#define LWM2M_DEVICE_MANUFACTURER 0 +#define LWM2M_DEVICE_MODEL_NUMBER 1 +#define LWM2M_DEVICE_SERIAL_NUMBER 2 +#define LWM2M_DEVICE_FIRMWARE_VERSION 3 +#define LWM2M_DEVICE_REBOOT 4 +#define LWM2M_DEVICE_FACTORY_RESET 5 +#define LWM2M_DEVICE_AVAILABLE_POWER_SOURCES 6 +#define LWM2M_DEVICE_POWER_SOURCE_VOLTAGE 7 +#define LWM2M_DEVICE_POWER_SOURCE_CURRENT 8 +#define LWM2M_DEVICE_BATTERY_LEVEL 9 +#define LWM2M_DEVICE_MEMORY_FREE 10 +#define LWM2M_DEVICE_ERROR_CODE 11 +#define LWM2M_DEVICE_RESET_ERROR_CODE 12 +#define LWM2M_DEVICE_CURRENT_TIME 13 +#define LWM2M_DEVICE_UTC_OFFSET 14 +#define LWM2M_DEVICE_TIMEZONE 15 +#define LWM2M_DEVICE_SUPPORTED_BINDINGS 16 + +/* LWM2M Location */ +#define LWM2M_LOCATION_LATITUDE 0 +#define LWM2M_LOCATION_LONGITUDE 1 +#define LWM2M_LOCATION_ALTITUDE 2 +#define LWM2M_LOCATION_UNCERTAINTY 3 +#define LWM2M_LOCATION_VELOCITY 4 +#define LWM2M_LOCATION_TIMESTAMP 5 + +/* LWM2M Software update */ +#define LWM2M_SW_UPDATE_PKG_NAME 0 +#define LWM2M_SW_UPDATE_PKG_VERSION 1 +#define LWM2M_SW_UPDATE_PACKAGE 2 +#define LWM2M_SW_UPDATE_PACKAGE_URI 3 +#define LWM2M_SW_UPDATE_INSTALL 4 +#define LWM2M_SW_UPDATE_CHECKPOINT 5 +#define LWM2M_SW_UPDATE_UNINSTALL 6 +#define LWM2M_SW_UPDATE_UPDATE_STATE 7 +#define LWM2M_SW_UPDATE_SUPPORTED_OBJECTS 8 + +/** + * LWM2M Enabler + */ + +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. MUST be first. */ + uint8_t operations[12]; /* Internal. MUST be second. */ + uint16_t resource_ids[12]; /* Internal. MUST be third. */ + + /* Public members. */ + lwm2m_string_t server_uri; + bool bootstrap_server; + uint8_t security_mode; + lwm2m_opaque_t public_key; + lwm2m_opaque_t server_public_key; + lwm2m_opaque_t secret_key; + uint8_t sms_security_mode; + lwm2m_opaque_t sms_binding_key_param; + lwm2m_opaque_t sms_binding_secret_keys; + uint32_t sms_number; + uint16_t short_server_id; + lwm2m_time_t client_hold_off_time; + +} lwm2m_security_t; + +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. MUST be first. */ + uint8_t operations[9]; /* Internal. MUST be second. */ + uint16_t resource_ids[9]; /* Internal. MUST be third. */ + + /* Public members. */ + uint16_t short_server_id; + lwm2m_time_t lifetime; + lwm2m_time_t default_minimum_period; + lwm2m_time_t default_maximum_period; + void * disable; // Function pointer. + lwm2m_time_t disable_timeout; + bool notification_storing_on_disabled; + lwm2m_string_t binding; + void * registration_update_trigger; // Function pointer. + +} lwm2m_server_t; + +typedef struct +{ + lwm2m_instance_prototype_t proto; /* Internal. MUST be first. */ + uint8_t operations[6]; /* Internal. MUST be second. */ + uint16_t resource_ids[6]; /* Internal. MUST be third. */ + + /* Public members. */ + lwm2m_opaque_t package; + lwm2m_string_t package_uri; + uint8_t state; + bool update_supported_objects; + uint8_t update_result; + +} lwm2m_firmware_t; + +typedef struct +{ + lwm2m_instance_prototype_t proto; + uint8_t operations[4]; + uint16_t resource_ids[4]; + + /* Public members. */ + uint16_t acl_object_id; + uint16_t acl_instance_id; + uint16_t acl; + uint16_t control_owner; + +} lwm2m_acl_t; + +typedef struct +{ + lwm2m_instance_prototype_t proto; + uint8_t operations[11]; + uint16_t resource_ids[11]; + + /* Public members. */ + uint32_t network_bearer; + uint32_t available_network_bearer;// TODO this is a list! + uint32_t radio_signal_strength; // Unit: dBm + uint32_t link_quality; + lwm2m_string_t ip_addresses; // TODO: this is a list! + lwm2m_string_t router_ip_addresses; // TODO: this is a list! + uint8_t link_utilization; // Unit: percent + lwm2m_string_t apn; // TODO: this is a list! + uint32_t cell_id; + uint8_t smnc; // Unit: percent + uint32_t smcc; + +} lwm2m_connectivity_monitoring_t; + +typedef struct +{ + lwm2m_instance_prototype_t proto; + uint8_t operations[7]; + uint16_t resource_ids[7]; + + /* Public members. */ + uint32_t sms_tx_counter; + uint32_t sms_rx_counter; + uint32_t tx_data; // Unit: kilo-bytes + uint32_t rx_data; // Unit: kilo-bytes + uint32_t max_message_size; // Unit: byte + uint32_t average_message_size; // Unit: byte + /* StartOrReset is Execute only */ + +} lwm2m_connectivity_statistics_t; + +typedef struct +{ + lwm2m_instance_prototype_t proto; + uint8_t operations[17]; + uint16_t resource_ids[17]; + + /* Public members. */ + lwm2m_string_t manufacturer; + lwm2m_string_t model_number; + lwm2m_string_t serial_number; + lwm2m_string_t firmware_version; + /* Reboot is execute only */ + /* Factory reset is execute only */ + uint8_t avail_power_sources; // TODO: this is a list, Range: 0-7 + uint32_t power_source_voltage; // TODO: this is a list, Unit: mV + uint32_t power_source_current; // TODO: this is a list, Unit: mA + uint8_t battery_level; // Unit: percent + uint32_t memory_free; // Unit: KB + uint32_t error_code; // TODO: this is a list + /* Reset Error code is execute only */ + lwm2m_time_t current_time; + lwm2m_string_t utc_offset; + lwm2m_string_t timezone; + lwm2m_string_t supported_bindings; // TODO this is a list + +} lwm2m_device_t; + +typedef struct +{ + lwm2m_instance_prototype_t proto; + uint8_t operations[6]; + uint16_t resource_ids[6]; + + /* Public members. */ + lwm2m_string_t latitude; // Unit: Deg + lwm2m_string_t longitude; // Unit: Deg + lwm2m_string_t altitude; // Unit: m + lwm2m_string_t uncertainty; // Unit: m + lwm2m_opaque_t velocity; // Unit: Refers to 3GPP GAD specs + lwm2m_time_t timestamp; // Range: 0-6 + +} lwm2m_location_t; + +typedef struct +{ + lwm2m_instance_prototype_t proto; + uint8_t operations[9]; + uint16_t resource_ids[9]; + + /* Public members. */ + lwm2m_string_t pkg_name; + lwm2m_string_t pkg_version; + lwm2m_opaque_t package; + lwm2m_string_t package_uri; + /* Install is execute only */ + uint16_t checkpoint; // TODO: this is of type Objlnk + /* Uninstall is execute only */ + uint8_t update_state; // Range: 1-5 + bool update_supported_objects; + +} lwm2m_software_update_t; + +/**@brief Initialize a LWM2M security object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void lwm2m_instance_security_init(lwm2m_security_t * p_instance); + +/**@brief Initialize a LWM2M server object instance. + * + * @details Must be called before any use of the instance. + + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void lwm2m_instance_server_init(lwm2m_server_t * p_instance); + +/**@brief Initialize a LWM2M firmware object instance. + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void lwm2m_instance_firmware_init(lwm2m_firmware_t * p_instance); + +/**@brief Initialize a LWM2M ACL object instance + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void lwm2m_instance_acl_init(lwm2m_acl_t * p_instance); + +/**@brief Initialize a LWM2M connectivity monitoring object instance + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void lwm2m_instance_connectivity_monitoring_init(lwm2m_connectivity_monitoring_t * p_instance); + +/**@brief Initialize a LWM2M connectivity statistics object instance + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void lwm2m_instance_connectivity_statistics_init(lwm2m_connectivity_statistics_t * p_instance); + +/**@brief Initialize a LWM2M device object instance + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void lwm2m_instance_device_init(lwm2m_device_t * p_instance); + +/**@brief Initialize a LWM2M location object instance + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void lwm2m_instance_location_init(lwm2m_location_t * p_instance); + +/**@brief Initialize a LWM2M software update object instance + * + * @details Must be called before any use of the instance. + * + * @param[in] p_instance Pointer to instance structure to initialize. + */ +void lwm2m_instance_software_update_init(lwm2m_software_update_t * p_instance); + +#ifdef __cplusplus +} +#endif + +#endif // LWM2M_OBJECTS_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects_tlv.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects_tlv.c new file mode 100644 index 0000000..93cb6c1 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects_tlv.c @@ -0,0 +1,498 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "lwm2m_objects_tlv.h" +#include "lwm2m_tlv.h" + +static void index_buffer_len_update(uint32_t * index, uint32_t * buffer_len, uint32_t max_buffer) +{ + *index += *buffer_len; + *buffer_len = max_buffer - *index; +} + +uint32_t lwm2m_tlv_server_decode(lwm2m_server_t * server, uint8_t * buffer, uint32_t buffer_len) +{ + uint32_t err_code; + lwm2m_tlv_t tlv; + + uint32_t index = 0; + + while (index < buffer_len) + { + err_code = lwm2m_tlv_decode(&tlv, &index, buffer, buffer_len); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + switch (tlv.id) + { + case LWM2M_SERVER_SHORT_SERVER_ID: + { + if (lwm2m_tlv_bytebuffer_to_uint16(tlv.value, tlv.length, &server->short_server_id) != NRF_SUCCESS) + { + return NRF_ERROR_INVALID_DATA; + } + + break; + } + + case LWM2M_SERVER_LIFETIME: + { + if (lwm2m_tlv_bytebuffer_to_uint32(tlv.value, tlv.length, &server->lifetime)) + { + return NRF_ERROR_INVALID_DATA; + } + break; + } + + case LWM2M_SERVER_DEFAULT_MIN_PERIOD: + { + if (lwm2m_tlv_bytebuffer_to_uint32(tlv.value, tlv.length, &server->default_minimum_period)) + { + return NRF_ERROR_INVALID_DATA; + } + break; + } + + case LWM2M_SERVER_DEFAULT_MAX_PERIOD: + { + if (lwm2m_tlv_bytebuffer_to_uint32(tlv.value, tlv.length, &server->default_maximum_period)) + { + return NRF_ERROR_INVALID_DATA; + } + break; + } + + case LWM2M_SERVER_DISABLE: + { + // Execute do nothing + break; + } + + case LWM2M_SERVER_DISABLE_TIMEOUT: + { + if (lwm2m_tlv_bytebuffer_to_uint32(tlv.value, tlv.length, &server->disable_timeout)) + { + return NRF_ERROR_INVALID_DATA; + } + break; + } + + case LWM2M_SERVER_NOTIFY_WHEN_DISABLED: + { + server->notification_storing_on_disabled = tlv.value[0]; + break; + } + case LWM2M_SERVER_BINDING: + { + // If original buffer is gone this will also be gone + server->binding.len = tlv.length; + server->binding.p_val = (char *) tlv.value; + break; + } + + case LWM2M_SERVER_REGISTRATION_UPDATE_TRIGGER: + { + // Execute do nothing + break; + } + + default: + break; + } + } + + return NRF_SUCCESS; +} + + +uint32_t lwm2m_tlv_security_decode(lwm2m_security_t * p_security, + uint8_t * p_buffer, + uint32_t buffer_len) +{ + uint32_t err_code; + lwm2m_tlv_t tlv; + + uint32_t index = 0; + + while (index < buffer_len) + { + err_code = lwm2m_tlv_decode(&tlv, &index, p_buffer, buffer_len); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + switch (tlv.id) + { + case LWM2M_SECURITY_SERVER_URI: + { + p_security->server_uri.p_val = (char *)tlv.value; + p_security->server_uri.len = tlv.length; + break; + } + + case LWM2M_SECURITY_BOOTSTRAP_SERVER: + { + p_security->bootstrap_server = tlv.value[0]; + break; + } + + case LWM2M_SECURITY_SECURITY_MODE: + { + p_security->security_mode = tlv.value[0]; + break; + } + + case LWM2M_SECURITY_PUBLIC_KEY: + { + p_security->public_key.p_val = tlv.value; + p_security->public_key.len = tlv.length; + break; + } + + case LWM2M_SECURITY_SERVER_PUBLIC_KEY: + { + p_security->server_public_key.p_val = tlv.value; + p_security->server_public_key.len = tlv.length; + break; + } + + case LWM2M_SECURITY_SECRET_KEY: + { + p_security->secret_key.p_val = tlv.value; + p_security->secret_key.len = tlv.length; + break; + } + + case LWM2M_SECURITY_SMS_SECURITY_MODE: + { + p_security->sms_security_mode = tlv.value[0]; + break; + } + + case LWM2M_SECURITY_SMS_BINDING_KEY_PARAM: + { + p_security->sms_binding_key_param.p_val = tlv.value; + p_security->sms_binding_key_param.len = tlv.length; + break; + } + + case LWM2M_SECURITY_SMS_BINDING_SECRET_KEY: + { + p_security->sms_binding_secret_keys.p_val = tlv.value; + p_security->sms_binding_secret_keys.len = tlv.length; + break; + } + + case LWM2M_SECURITY_SERVER_SMS_NUMBER: + { + uint32_t result = lwm2m_tlv_bytebuffer_to_uint32(tlv.value, + tlv.length, + &p_security->sms_number); + if (result != NRF_SUCCESS) + { + return NRF_ERROR_INVALID_DATA; + } + break; + } + + case LWM2M_SECURITY_SHORT_SERVER_ID: + { + uint32_t result = lwm2m_tlv_bytebuffer_to_uint16(tlv.value, + tlv.length, + &p_security->short_server_id); + if (result != NRF_SUCCESS) + { + return NRF_ERROR_INVALID_DATA; + } + break; + } + + case LWM2M_SECURITY_CLIENT_HOLD_OFF_TIME: + { + uint32_t result = lwm2m_tlv_bytebuffer_to_uint32(tlv.value, + tlv.length, + &p_security->client_hold_off_time); + if (result != NRF_SUCCESS) + { + return NRF_ERROR_INVALID_DATA; + } + break; + } + + default: + break; + } + } + + return NRF_SUCCESS; +} + + +uint32_t lwm2m_tlv_server_encode(uint8_t * p_buffer, + uint32_t * p_buffer_len, + lwm2m_server_t * p_server) +{ + uint32_t err_code; + uint32_t max_buffer = *p_buffer_len; + uint32_t index = 0; + + lwm2m_tlv_t tlv; + tlv.id_type = TLV_TYPE_RESOURCE_VAL; // Type is the same for all. + + // Encode short server id. + lwm2m_tlv_uint16_set(&tlv, p_server->short_server_id, LWM2M_SERVER_SHORT_SERVER_ID); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + // Encode lifetime. + lwm2m_tlv_uint32_set(&tlv, p_server->lifetime, LWM2M_SERVER_LIFETIME); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + // Encode default minimum period. + lwm2m_tlv_uint32_set(&tlv, p_server->default_minimum_period, LWM2M_SERVER_DEFAULT_MIN_PERIOD); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + // Encode default maximum period. + lwm2m_tlv_uint32_set(&tlv, p_server->default_maximum_period, LWM2M_SERVER_DEFAULT_MAX_PERIOD); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + // Encode disable timeout. + lwm2m_tlv_uint32_set(&tlv, p_server->disable_timeout, LWM2M_SERVER_DISABLE_TIMEOUT); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + // Encode Notify when disabled. + lwm2m_tlv_bool_set(&tlv, p_server->notification_storing_on_disabled, LWM2M_SERVER_NOTIFY_WHEN_DISABLED); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + // Encode binding. + lwm2m_tlv_string_set(&tlv, p_server->binding, LWM2M_SERVER_BINDING); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + *p_buffer_len = index; + + return NRF_SUCCESS; +} + + +uint32_t lwm2m_tlv_security_encode(uint8_t * p_buffer, + uint32_t * p_buffer_len, + lwm2m_security_t * p_security) +{ + uint32_t err_code; + uint32_t max_buffer = *p_buffer_len; + uint32_t index = 0; + + lwm2m_tlv_t tlv; + tlv.id_type = TLV_TYPE_RESOURCE_VAL; // type is the same for all. + + + lwm2m_tlv_string_set(&tlv, p_security->server_uri, LWM2M_SECURITY_SERVER_URI); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_bool_set(&tlv, p_security->bootstrap_server, LWM2M_SECURITY_BOOTSTRAP_SERVER); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_uint16_set(&tlv, p_security->security_mode, LWM2M_SECURITY_SECURITY_MODE); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_opaque_set(&tlv, p_security->public_key, LWM2M_SECURITY_PUBLIC_KEY); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_opaque_set(&tlv, p_security->server_public_key, LWM2M_SECURITY_SERVER_PUBLIC_KEY); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_opaque_set(&tlv, p_security->secret_key, LWM2M_SECURITY_SECRET_KEY); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_uint16_set(&tlv, p_security->sms_security_mode, LWM2M_SECURITY_SMS_SECURITY_MODE); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_opaque_set(&tlv, p_security->sms_binding_key_param, LWM2M_SECURITY_SMS_BINDING_KEY_PARAM); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_opaque_set(&tlv, p_security->sms_binding_secret_keys, LWM2M_SECURITY_SMS_BINDING_SECRET_KEY); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_uint32_set(&tlv, p_security->sms_number, LWM2M_SECURITY_SERVER_SMS_NUMBER); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_uint16_set(&tlv, p_security->short_server_id, LWM2M_SECURITY_SHORT_SERVER_ID); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + lwm2m_tlv_uint32_set(&tlv, p_security->client_hold_off_time, LWM2M_SECURITY_CLIENT_HOLD_OFF_TIME); + err_code = lwm2m_tlv_encode(p_buffer + index, p_buffer_len, &tlv); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + index_buffer_len_update(&index, p_buffer_len, max_buffer); + + return NRF_SUCCESS; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects_tlv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects_tlv.h new file mode 100644 index 0000000..608ecef --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_objects_tlv.h @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file lwm2m_objects_tlv.h + * + * @defgroup iot_sdk_lwm2m_objects_tlv OMA LWM2M object TLV encoder and decoder API + * @ingroup iot_sdk_lwm2m + * @{ + * @brief OMA LWM2M object TLV encoder and decoder API. + */ + +#ifndef LWM2M_OBJECTS_TLV_H__ +#define LWM2M_OBJECTS_TLV_H__ + +#include <stdint.h> +#include "lwm2m_objects.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Decode a LWM2M server object from a TLV byte buffer. + * + * @note Resource values NOT found in the tlv will not be altered. + * + * @warning lwm2m_string_t and lwm2m_opaque_t values will point to the byte buffer and needs + * to be copied by the application before the byte buffer is freed. + * + * @param[out] p_server Pointer to a LWM2M server object to be filled by the decoded TLVs. + * @param[in] p_buffer Pointer to the TLV byte buffer to be decoded. + * @param[in] buffer_len Size of the buffer to be decoded. + * + * @retval NRF_SUCCESS If decoding was successful. + */ +uint32_t lwm2m_tlv_server_decode(lwm2m_server_t * p_server, + uint8_t * p_buffer, + uint32_t buffer_len); + +/**@brief Encode a LWM2M server object to a TLV byte buffer. + * + * @param[out] p_buffer Pointer to a byte buffer to be used to fill the encoded TLVs. + * @param[inout] p_buffer_len Value by reference indicating the size of the buffer provided. + * Will return the number of used bytes on return. + * @param[in] p_server Pointer to the LWM2M server object to be encoded into TLVs. + * + * @retval NRF_SUCCESS If the encoded was successful. + */ +uint32_t lwm2m_tlv_server_encode(uint8_t * p_buffer, + uint32_t * p_buffer_len, + lwm2m_server_t * p_server); + +/**@brief Decode a LWM2M security object from a TLV byte buffer. + * + * @note Resource values NOT found in the tlv will not be altered. + * + * @warning lwm2m_string_t and lwm2m_opaque_t values will point to the byte buffer and needs + * to be copied by the application before the byte buffer is freed. + * + * @param[out] p_security Pointer to a LWM2M server object to be filled by the decoded TLVs. + * @param[in] p_buffer Pointer to the TLV byte buffer to be decoded. + * @param[in] buffer_len Size of the buffer to be decoded. + * + * @retval NRF_SUCCESS If decoding was successful. + */ +uint32_t lwm2m_tlv_security_decode(lwm2m_security_t * p_security, + uint8_t * p_buffer, + uint32_t buffer_len); + +/**@brief Encode a LWM2M security object to a TLV byte buffer. + * + * @param[out] p_buffer Pointer to a byte buffer to be used to fill the encoded TLVs. + * @param[inout] p_buffer_len Value by reference indicating the size of the buffer provided. + * Will return the number of used bytes on return. + * @param[in] p_security Pointer to the LWM2M security object to be encoded into TLVs. + * + * @retval NRF_SUCCESS If the encoded was successful. + */ +uint32_t lwm2m_tlv_security_encode(uint8_t * p_buffer, + uint32_t * p_buffer_len, + lwm2m_security_t * p_security); + +#ifdef __cplusplus +} +#endif + +#endif // LWM2M_OBJECTS_TLV_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_register.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_register.c new file mode 100644 index 0000000..3c273ee --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_register.c @@ -0,0 +1,542 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include "lwm2m_api.h" +#include "lwm2m_register.h" +#include "lwm2m.h" +#include "coap_api.h" +#include "coap_message.h" +#include "coap_codes.h" +#include "sdk_config.h" +#include "app_util.h" + +#if LWM2M_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME lwm2m + +#define NRF_LOG_LEVEL LWM2M_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR LWM2M_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR LWM2M_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" + +#define LWM2M_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define LWM2M_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define LWM2M_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define LWM2M_ENTRY() LWM2M_TRC(">> %s", __func__) +#define LWM2M_EXIT() LWM2M_TRC("<< %s", __func__) + +#else // LWM2M_CONFIG_LOG_ENABLED + +#define LWM2M_TRC(...) /**< Disables traces. */ +#define LWM2M_DUMP(...) /**< Disables dumping of octet streams. */ +#define LWM2M_ERR(...) /**< Disables error logs. */ + +#define LWM2M_ENTRY(...) +#define LWM2M_EXIT(...) + +#endif // LWM2M_CONFIG_LOG_ENABLED + +#define LWM2M_REGISTER_URI_PATH "rd" + +#define TOKEN_START 0xAE1C + +typedef struct +{ + lwm2m_remote_t remote; + char location[LWM2M_REGISTER_MAX_LOCATION_LEN]; + uint16_t location_len; +} internal_lwm2m_remote_location_t; + + +static internal_lwm2m_remote_location_t m_remote_to_location[LWM2M_MAX_SERVERS]; + +static uint16_t num_servers = 0; +static uint16_t m_token = TOKEN_START; + +static uint32_t internal_message_new(coap_message_t ** pp_msg, + coap_msg_code_t code, + coap_response_callback_t callback, + uint16_t local_port) +{ + uint32_t err_code; + coap_message_conf_t conf; + memset (&conf, 0, sizeof(coap_message_conf_t)); + + conf.type = COAP_TYPE_CON; + conf.code = code; + conf.response_callback = callback; + conf.port.port_number = local_port; + + conf.token_len = uint16_encode(m_token, conf.token); + + m_token++; + + err_code = coap_message_new(pp_msg, &conf); + + return err_code; +} + + +static uint32_t internal_location_find(lwm2m_string_t * p_location, lwm2m_remote_t * p_remote) +{ + for (uint16_t i = 0; i < num_servers; ++i) + { + if (memcmp(&m_remote_to_location[i].remote, p_remote, sizeof(lwm2m_remote_t)) == 0) + { + p_location->p_val = m_remote_to_location[i].location; + p_location->len = m_remote_to_location[i].location_len; + return NRF_SUCCESS; + } + } + + return NRF_ERROR_NOT_FOUND; +} + + +static uint32_t internal_remote_location_save(lwm2m_string_t * location, lwm2m_remote_t * remote) +{ + for (uint16_t i = 0; i < num_servers; ++i) + { + if (memcmp(&m_remote_to_location[i].remote, remote, sizeof(lwm2m_remote_t)) == 0) + { + memcpy(m_remote_to_location[i].location, location->p_val, location->len); + m_remote_to_location[i].location_len = location->len; + return NRF_SUCCESS; + } + } + + if (num_servers == LWM2M_MAX_SERVERS) + return NRF_ERROR_NO_MEM; + + memcpy(&m_remote_to_location[num_servers].remote, remote, sizeof(lwm2m_remote_t)); + + memcpy(m_remote_to_location[num_servers].location, location->p_val, location->len); + + m_remote_to_location[num_servers].location_len = location->len; + + ++num_servers; + + return NRF_SUCCESS; +} + + +static uint32_t internal_server_config_set(coap_message_t * msg, lwm2m_server_config_t * p_config) +{ + char buffer[32]; + uint32_t err_code = NRF_SUCCESS; + + if (p_config->lifetime > 0) + { + int retval = snprintf(buffer, sizeof(buffer), "lt=%lu", p_config->lifetime); + if (retval < 0) + { + err_code = NRF_ERROR_INVALID_PARAM; + } + else + { + err_code = coap_message_opt_str_add(msg, + COAP_OPT_URI_QUERY, + (uint8_t *)buffer, + strlen(buffer)); + } + } + + if (err_code == NRF_SUCCESS) + { + if ((p_config->lwm2m_version_major > 0) || (p_config->lwm2m_version_minor > 0)) + { + int retval = snprintf(buffer, + sizeof(buffer), + "lwm2m=%d.%d", + p_config->lwm2m_version_major, + p_config->lwm2m_version_minor); + if (retval < 0) + { + err_code = NRF_ERROR_INVALID_PARAM; + } + else + { + err_code = coap_message_opt_str_add(msg, + COAP_OPT_URI_QUERY, + (uint8_t *)buffer, + strlen(buffer)); + } + } + } + + if (err_code == NRF_SUCCESS) + { + if (p_config->msisdn > 0) + { + int retval = snprintf(buffer, sizeof(buffer), "sms=%llu" , p_config->msisdn); + if (retval < 0) + { + err_code = NRF_ERROR_INVALID_PARAM; + } + else + { + err_code = coap_message_opt_str_add(msg, + COAP_OPT_URI_QUERY, + (uint8_t *)buffer, + strlen(buffer)); + } + } + } + + if (err_code == NRF_SUCCESS) + { + if (p_config->binding.len > 0) + { + if (p_config->binding.len < sizeof(buffer) - 2) + { + buffer[0] = 'b'; + buffer[1] = '='; + memcpy(buffer + 2, p_config->binding.p_val, p_config->binding.len); + + err_code = coap_message_opt_str_add(msg, + COAP_OPT_URI_QUERY, + (uint8_t *)buffer, + p_config->binding.len + 2); + } + else + { + err_code = NRF_ERROR_NO_MEM; + } + } + } + + return err_code; +} + + +uint32_t internal_lwm2m_register_init(void) +{ + m_token = TOKEN_START; + num_servers = 0; + return NRF_SUCCESS; +} + + +static void lwm2m_register_cb(uint32_t status, void * p_arg, coap_message_t * p_message) +{ + LWM2M_TRC("[Register]: lwm2m_register_cb, status: %ul, coap code: %u", + status, + p_message->header.code); + + LWM2M_MUTEX_LOCK(); + + for (uint16_t i = 0; i < p_message->options_count; ++i) + { + coap_option_t option = p_message->options[i]; + + if (option.number == COAP_OPT_LOCATION_PATH) + { + lwm2m_string_t location; + location.p_val = (char *) option.p_data; + location.len = option.length; + (void)internal_remote_location_save(&location, &p_message->remote); + } + } + + LWM2M_MUTEX_UNLOCK(); + + (void)lwm2m_notification(LWM2M_NOTIFCATION_TYPE_REGISTER, + &p_message->remote, + p_message->header.code); +} + + +uint32_t lwm2m_register(lwm2m_remote_t * p_remote, + lwm2m_client_identity_t * p_id, + lwm2m_server_config_t * p_config, + uint16_t local_port, + uint8_t * p_link_format_string, + uint16_t link_format_len) +{ + LWM2M_ENTRY(); + + NULL_PARAM_CHECK(p_remote); + NULL_PARAM_CHECK(p_id); + NULL_PARAM_CHECK(p_config); + NULL_PARAM_CHECK(p_link_format_string); + + LWM2M_MUTEX_LOCK(); + + uint32_t err_code; + char buffer[40]; + + lwm2m_string_t endpoint; + + endpoint.p_val = LWM2M_REGISTER_URI_PATH; + endpoint.len = 2; + + coap_message_t * p_msg; + + err_code = internal_message_new(&p_msg, COAP_CODE_POST, lwm2m_register_cb, local_port); + if (err_code != NRF_SUCCESS) + { + LWM2M_MUTEX_UNLOCK(); + return err_code; + } + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_remote_addr_set(p_msg, p_remote); + } + + if (err_code == NRF_SUCCESS) + { + // Set uri-path option + err_code = coap_message_opt_str_add(p_msg, + COAP_OPT_URI_PATH, + (uint8_t *)endpoint.p_val, + endpoint.len); // end_point length is always 2 + } + + if (err_code == NRF_SUCCESS) + { + // Set content format. + err_code = coap_message_opt_uint_add(p_msg, COAP_OPT_CONTENT_FORMAT, COAP_CT_APP_LINK_FORMAT); + } + + if (err_code == NRF_SUCCESS) + { + // Set queries. + buffer[0] = 'e'; + buffer[1] = 'p'; + buffer[2] = '='; + memcpy(buffer + 3, &p_id->value, p_id->type); + + err_code = coap_message_opt_str_add(p_msg, + COAP_OPT_URI_QUERY, + (uint8_t *)buffer, + p_id->type + 3); + } + + if (err_code == NRF_SUCCESS) + { + err_code = internal_server_config_set(p_msg, p_config); + } + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_payload_set(p_msg, p_link_format_string, link_format_len); + } + + if (err_code == NRF_SUCCESS) + { + uint32_t msg_handle; + err_code = coap_message_send(&msg_handle, p_msg); + } + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_delete(p_msg); + } + else + { + // If we have hit an error try to clean up. + // Return the original error code. + (void)coap_message_delete(p_msg); + } + + LWM2M_MUTEX_UNLOCK(); + + LWM2M_EXIT(); + + return err_code; +} + +void lwm2m_update_cb(uint32_t status, void * p_arg, coap_message_t * p_message) +{ + LWM2M_TRC("[Update]: lwm2m_update_cb, status: %ul, coap code: %u", + status, + p_message->header.code); + + (void)lwm2m_notification(LWM2M_NOTIFCATION_TYPE_UPDATE, + &p_message->remote, + p_message->header.code); +} + + +uint32_t lwm2m_update(lwm2m_remote_t * p_remote, lwm2m_server_config_t * p_config, uint16_t local_port) +{ + LWM2M_ENTRY(); + + NULL_PARAM_CHECK(p_remote); + NULL_PARAM_CHECK(p_config); + + LWM2M_MUTEX_LOCK(); + + uint32_t err_code; + coap_message_t * p_msg; + + err_code = internal_message_new(&p_msg, COAP_CODE_POST, lwm2m_update_cb, local_port); + if (err_code != NRF_SUCCESS) + { + LWM2M_MUTEX_UNLOCK(); + return err_code; + } + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_remote_addr_set(p_msg, p_remote); + } + + if (err_code == NRF_SUCCESS) + { + lwm2m_string_t location; + err_code = internal_location_find(&location, p_remote); + + if (err_code == NRF_SUCCESS) + { + // Sets URI PATH + err_code = coap_message_opt_str_add(p_msg, + COAP_OPT_URI_PATH, + (uint8_t *)location.p_val, + location.len); + } + } + + if (err_code == NRF_SUCCESS) + { + // Sets CoAP queries + err_code = internal_server_config_set(p_msg, p_config); + } + + if (err_code == NRF_SUCCESS) + { + uint32_t msg_handle; + err_code = coap_message_send(&msg_handle, p_msg); + } + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_delete(p_msg); + } + else + { + // If we have hit an error try to clean up. + // Return the original error code. + (void)coap_message_delete(p_msg); + } + + LWM2M_EXIT(); + + LWM2M_MUTEX_UNLOCK(); + + return err_code; +} + +void lwm2m_deregister_cb(uint32_t status, void * p_arg, coap_message_t * p_message) +{ + LWM2M_TRC("[DeRegister]: lwm2m_deregister_cb, status: %ul, coap code: %u", + status, + p_message->header.code); + + (void)lwm2m_notification(LWM2M_NOTIFCATION_TYPE_DEREGISTER, + &p_message->remote, + p_message->header.code); +} + +uint32_t lwm2m_deregister(lwm2m_remote_t * p_remote, uint16_t local_port) +{ + LWM2M_ENTRY(); + + NULL_PARAM_CHECK(p_remote); + + LWM2M_MUTEX_LOCK(); + + uint32_t err_code; + coap_message_t * p_msg; + + err_code = internal_message_new(&p_msg, COAP_CODE_DELETE, lwm2m_deregister_cb, local_port); + if (err_code != NRF_SUCCESS) + { + LWM2M_MUTEX_UNLOCK(); + return err_code; + } + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_remote_addr_set(p_msg, p_remote); + } + + if (err_code == NRF_SUCCESS) + { + lwm2m_string_t location; + err_code = internal_location_find(&location, p_remote); + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_opt_str_add(p_msg, + COAP_OPT_URI_PATH, + (uint8_t *)location.p_val, + location.len); + } + } + + if (err_code == NRF_SUCCESS) + { + uint32_t msg_handle; + err_code = coap_message_send(&msg_handle, p_msg); + } + + if (err_code == NRF_SUCCESS) + { + err_code = coap_message_delete(p_msg); + } + else + { + // If we have hit an error try to clean up. + // Return the original error code. + (void)coap_message_delete(p_msg); + } + + LWM2M_EXIT(); + + LWM2M_MUTEX_UNLOCK(); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_register.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_register.h new file mode 100644 index 0000000..5daa950 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_register.h @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file lwm2m_register.h + * + * @defgroup iot_sdk_lwm2m_register_api LWM2M register API interface + * @ingroup iot_sdk_lwm2m + * @{ + * @brief Register API interface for the LWM2M protocol. + */ + +#ifndef LWM2M_REGISTER_H__ +#define LWM2M_REGISTER_H__ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Initialize the LWM2M register module. + * + * @details Calling this function will set the module in default state. + */ +uint32_t internal_lwm2m_register_init(void); + +#ifdef __cplusplus +} +#endif + +#endif // LWM2M_REGISTER_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_tlv.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_tlv.c new file mode 100644 index 0000000..047cdb7 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_tlv.c @@ -0,0 +1,399 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdint.h> +#include <string.h> +#include "lwm2m_tlv.h" +#include "lwm2m_objects.h" +#include "iot_errors.h" +#include "iot_defines.h" + +// Used for encoding +// TODO: Remove this temp_buffer in order to allow to users to use the API at the same time. +// Current implementation might fail if two different interrupt levels are executing +// encode at the same time. The temp_buffer will be overwritten by the last user. +static uint8_t temp_buffer[4]; + + +uint32_t lwm2m_tlv_bytebuffer_to_uint32(uint8_t * p_buffer, uint8_t val_len, uint32_t * p_result) +{ + uint32_t res; + switch (val_len) + { + case 0: + { + res = 0; + break; + } + + case 1: + { + res = p_buffer[0]; + break; + } + + case 2: + { + res = ((uint32_t)p_buffer[0] << 8) | + p_buffer[1]; + break; + } + + case 3: + { + res = ((uint32_t)p_buffer[0] << 16) | + ((uint32_t)p_buffer[1] << 8) | + p_buffer[2]; + break; + } + + case 4: + { + res = ((uint32_t)p_buffer[0] << 24) | + ((uint32_t)p_buffer[1] << 16) | + ((uint32_t)p_buffer[2] << 8) | + p_buffer[3]; + break; + } + + default: + return NRF_ERROR_DATA_SIZE; + } + + *p_result = res; + return NRF_SUCCESS; +} + + +uint32_t lwm2m_tlv_bytebuffer_to_uint16(uint8_t * p_buffer, uint8_t val_len, uint16_t * p_result) +{ + uint16_t res; + switch (val_len) + { + case 0: + { + res = 0; + break; + } + + case 1: + { + res = p_buffer[0]; + break; + } + + case 2: + { + res = ((uint16_t)p_buffer[0] << 8) | p_buffer[1]; + break; + } + + default: + return NRF_ERROR_DATA_SIZE; + } + + *p_result = res; + return NRF_SUCCESS; +} + + +void lwm2m_tlv_uint16_to_bytebuffer(uint8_t * p_buffer, uint8_t * p_len, uint16_t value) +{ + if (value == 0) + { + *p_len = 0; + } + else if (value <= UINT8_MAX) + { + p_buffer[0] = value; + *p_len = 1; + } + else + { + + p_buffer[1] = value; + p_buffer[0] = value >> 8; + *p_len = 2; + } +} + + +void lwm2m_tlv_uint32_to_bytebuffer(uint8_t * p_buffer, uint8_t * p_len, uint32_t value) +{ + if (value == 0) + { + *p_len = 0; + } + else if (value <= UINT8_MAX) + { + p_buffer[0] = value; + *p_len = 1; + } + else if (value <= UINT16_MAX) + { + + p_buffer[1] = value; + p_buffer[0] = value >> 8; + *p_len = 2; + } + else if (value <= 0xFFFFFF) // 24 bit + { + p_buffer[2] = value; + p_buffer[1] = value >> 8; + p_buffer[0] = value >> 16; + *p_len = 3; + } + else + { + p_buffer[3] = value; + p_buffer[2] = value >> 8; + p_buffer[1] = value >> 16; + p_buffer[0] = value >> 24; + *p_len = 4; + } +} + + +void lwm2m_tlv_uint16_set(lwm2m_tlv_t * p_tlv, uint16_t value, uint16_t id) +{ + uint8_t val_len; + lwm2m_tlv_uint16_to_bytebuffer(temp_buffer, &val_len, value); + + p_tlv->length = val_len; + p_tlv->value = temp_buffer; + p_tlv->id = id; +} + + +void lwm2m_tlv_uint32_set(lwm2m_tlv_t * p_tlv, uint32_t value, uint16_t id) +{ + uint8_t val_len; + lwm2m_tlv_uint32_to_bytebuffer(temp_buffer, &val_len, value); + + p_tlv->length = val_len; + p_tlv->value = temp_buffer; + p_tlv->id = id; +} + + +void lwm2m_tlv_bool_set(lwm2m_tlv_t * p_tlv, bool value, uint16_t id) +{ + + if (value == true) + { + temp_buffer[0] = 1; + } + else + { + temp_buffer[0] = 0; + } + + p_tlv->length = 1; + p_tlv->value = temp_buffer; + p_tlv->id = id; +} + + +void lwm2m_tlv_string_set(lwm2m_tlv_t * p_tlv, lwm2m_string_t string, uint16_t id) +{ + p_tlv->length = string.len; + p_tlv->value = (uint8_t *)string.p_val; + p_tlv->id = id; +} + + +void lwm2m_tlv_opaque_set(lwm2m_tlv_t * p_tlv, lwm2m_opaque_t opaque, uint16_t id) +{ + p_tlv->length = opaque.len; + p_tlv->value = opaque.p_val; + p_tlv->id = id; +} + + +uint32_t lwm2m_tlv_decode(lwm2m_tlv_t * p_tlv, + uint32_t * p_index, + uint8_t * p_buffer, + uint16_t buffer_len) +{ + uint32_t err_code; + uint16_t index = *p_index; + + uint8_t type = (p_buffer[index] & TLV_TYPE_MASK) >> TLV_TYPE_BIT_POS; + uint8_t id_len = (p_buffer[index] & TLV_ID_LEN_MASK) >> TLV_ID_LEN_BIT_POS; + uint8_t length_len = (p_buffer[index] & TLV_LEN_TYPE_MASK) >> TLV_LEN_TYPE_BIT_POS; + uint32_t length = (p_buffer[index] & TLV_LEN_VAL_MASK) >> TLV_VAL_LEN_BIT_POS; + + p_tlv->id_type = type; + p_tlv->length = 0; + + // Jump to the byte following the "Type" at index 0. + ++index; + + // Extract the Identifier based on the number of bytes indicated in id_len (bit 5). + // Adding one to the id_len will give the number of bytes used. + uint8_t id_len_size = id_len + 1; + + err_code = lwm2m_tlv_bytebuffer_to_uint16(&p_buffer[index], id_len_size, &p_tlv->id); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + index += id_len_size; + + + // Extract the value length. + // The length_len tells how many bytes are being used. + if (length_len == TLV_LEN_TYPE_3BIT) + { + p_tlv->length = length; + } + else + { + err_code = lwm2m_tlv_bytebuffer_to_uint32(&p_buffer[index], length_len, &length); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + p_tlv->length = length; + index += length_len; + } + + if (p_tlv->length > buffer_len) + { + return (IOT_LWM2M_ERR_BASE | NRF_ERROR_INVALID_DATA); + } + + p_tlv->value = &p_buffer[index]; + + *p_index = index + p_tlv->length; + + return NRF_SUCCESS; +} + + +uint32_t lwm2m_tlv_encode(uint8_t * p_buffer, uint32_t * buffer_len, lwm2m_tlv_t * p_tlv) +{ + uint8_t length_len; + uint8_t id_len; + + uint8_t id[2] = {0,}; + uint8_t len[3] = {0,}; + uint16_t index = 0; + uint8_t type = 0; + + // Set Identifier type by copying the lwm2m_tlv_t->id_type into bit 7-6. + type = (p_tlv->id_type << TLV_TYPE_BIT_POS); + + // Set length of Identifier in bit 5 in the TLV type byte. + if (p_tlv->id > UINT8_MAX) + { + type |= (TLV_ID_LEN_16BIT << TLV_ID_LEN_BIT_POS); + id[0] = p_tlv->id >> 8; + id[1] = p_tlv->id; + id_len = 2; + } + else + { + type |= (TLV_ID_LEN_8BIT << TLV_ID_LEN_BIT_POS); + id[0] = p_tlv->id; + id_len = 1; + } + + // Set type of Length bit 4-3 in the TLV type byte. + + // If the Length can fit into 3 bits. + if ((p_tlv->length & TLV_LEN_VAL_MASK) == p_tlv->length) + { + type |= (TLV_LEN_TYPE_3BIT << TLV_LEN_TYPE_BIT_POS); + length_len = 0; + + // As Length type field is set to "No Length", set bit 2-0. + type |= (p_tlv->length & TLV_LEN_VAL_MASK); + } + else + { + lwm2m_tlv_uint32_to_bytebuffer(&len[0], &length_len, p_tlv->length); + + // Length can not be larger than 24-bit. + if (length_len > TLV_LEN_TYPE_24BIT) + { + return (IOT_LWM2M_ERR_BASE | NRF_ERROR_INVALID_PARAM); + } + + type |= (length_len << TLV_LEN_TYPE_BIT_POS); + } + + // Check if the buffer is large enough. + if (*buffer_len < (p_tlv->length + id_len + length_len + 1)) // + 1 for the type byte + { + return (IOT_LWM2M_ERR_BASE | NRF_ERROR_DATA_SIZE); + } + + // Copy the type to the buffer. + memcpy(p_buffer + index, &type, 1); + ++index; + + // Copy the Identifier to the buffer. + memcpy(p_buffer + index, id, id_len); + index += id_len; + + // Copy length to the buffer. + if (length_len != 0) + { + memcpy(p_buffer + index, len, length_len); + index += length_len; + } + + // Copy the value to buffer, memcpy of 0 length is undefined behavior so lets avoid it. + if (p_tlv->length > 0) + { + memcpy(p_buffer + index, p_tlv->value, p_tlv->length); + } + + // Set length of the output buffer. + *buffer_len = p_tlv->length + index; + + return NRF_SUCCESS; +} + + + + diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_tlv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_tlv.h new file mode 100644 index 0000000..8ac5784 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/lwm2m/lwm2m_tlv.h @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file lwm2m_tlv.h + * + * @defgroup iot_sdk_lwm2m_tlv_api LWM2M TLV interface + * @ingroup iot_sdk_lwm2m + * @{ + * @brief TLV encoding and decoding interface for the LWM2M protocol. + */ + +#ifndef LWM2M_TLV_H__ +#define LWM2M_TLV_H__ + +#include <stdint.h> +#include "lwm2m_objects.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* TLV type masks +*/ +#define TLV_TYPE_BIT_POS 6 +#define TLV_ID_LEN_BIT_POS 5 +#define TLV_LEN_TYPE_BIT_POS 3 +#define TLV_VAL_LEN_BIT_POS 0 + +#define TLV_TYPE_MASK (0x3 << TLV_TYPE_BIT_POS) /**< Type bitmask, bit 7-6 (0b11000000). */ +#define TLV_ID_LEN_MASK (0x1 << TLV_ID_LEN_BIT_POS) /**< Length bitmask, bit 5 (0b00100000). */ +#define TLV_LEN_TYPE_MASK (0x3 << TLV_LEN_TYPE_BIT_POS) /**< Length type bitmask, bit 4-3 (0b00011000). */ +#define TLV_LEN_VAL_MASK (0x7 << TLV_VAL_LEN_BIT_POS) /**< Length of the value bitmask, bit 2-0 (0b00000111). */ + +#define TLV_TYPE_OBJECT 0x00 +#define TLV_TYPE_RESOURCE_INSTANCE 0x01 +#define TLV_TYPE_MULTI_RESOURCE 0x02 +#define TLV_TYPE_RESOURCE_VAL 0x03 + +#define TLV_ID_LEN_8BIT 0x00 +#define TLV_ID_LEN_16BIT 0x01 + +#define TLV_LEN_TYPE_3BIT 0x00 +#define TLV_LEN_TYPE_8BIT 0x01 +#define TLV_LEN_TYPE_16BIT 0x02 +#define TLV_LEN_TYPE_24BIT 0x03 + +typedef struct +{ + uint16_t id_type; /**< Identifier type. */ + uint16_t id; /**< Identifier ID. */ + uint32_t length; /**< Length of the value in the TLV. */ + uint8_t * value; /**< Value of the TLV. */ +} lwm2m_tlv_t; + +/**@brief Decode a LWM2M TLV byte buffer into a TLV structure. + * + * @param[out] p_tlv This struct will be filled with id, length, type and pointer to value. + * @param[inout] p_index Index to start decoding from. + * @param[in] p_buffer The buffer to decode from. + * @param[in] buffer_len The length of the buffer. + * + * @retval NRF_SUCCESS If decoding was successful. + * @retval IOT_LWM2M_ERR_BASE | NRF_INVALID_DATA + */ +uint32_t lwm2m_tlv_decode(lwm2m_tlv_t * p_tlv, uint32_t * p_index, uint8_t * p_buffer, uint16_t buffer_len); + +/**@brief Encode a TLV structure into a LWM2M TLV byte buffer. + * + * @details Encode using the provided tlv, if the buffer provided is to small an NRF_ERROR_DATA_SIZE will be returned. + * + * Maximum buffer size requirement: value_length + 6 (1 for type byte, 2 for id bytes, 3 for length bytes). + * + * @param[out] p_buffer Buffer to put the encoded tlv into. + * @param[inout] buffer_len Length of input buffer out: length of the encoded buffer. + * @param[in] p_tlv The tlv to use. + * + * @retval NRF_SUCCESS If decoding was successful. + * @retval IOT_LWM2M_ERR_BASE | NRF_ERROR_DATA_SIZE + */ +uint32_t lwm2m_tlv_encode(uint8_t * p_buffer, uint32_t * p_buffer_len, lwm2m_tlv_t * p_tlv); + +/**@brief Encode a byte buffer into a uint32_t. + * + * @param[in] p_buffer Buffer which holds a serialized version of the uint32_t. + * @param[in] val_len Length of the value in the buffer. + * @param[out] p_result By reference pointer to the result uint32_t. + * + * @retval NRF_SUCCESS If the conversion from byte buffer to uint32_t value was successful. + */ +uint32_t lwm2m_tlv_bytebuffer_to_uint32(uint8_t * p_buffer, uint8_t val_len, uint32_t * p_result); + +/**@brief Encode a byte buffer into a uint16_t. + * + * @param[in] p_buffer Buffer which holds a serialized version of the uint16_t. + * @param[in] val_len Length of the value in the buffer. + * @param[out] p_result By reference pointer to the result uint16_t. + * + * @retval NRF_SUCCESS If the conversion from byte buffer to uint32_t value was successful. + */ +uint32_t lwm2m_tlv_bytebuffer_to_uint16(uint8_t * p_buffer, uint8_t val_len, uint16_t * p_result); + +/**@brief Decode a uint32_t into a byte buffer. + * + * @param[out] p_buffer Buffer which holds a serialized version of the uint32_t. + * @param[out] p_len By reference pointer to hold the length of the serialized value in the buffer. + * @param[in] value Value to convert serialize into a byte buffer. + * + * @retval NRF_SUCCESS If the conversion from uint32_t value to byte buffer was successful. + */ +void lwm2m_tlv_uint32_to_bytebuffer(uint8_t * p_buffer, uint8_t * p_len, uint32_t value); + +/**@brief Decode a uint16_t into a byte buffer. + * + * @param[out] p_buffer Buffer which holds a serialized version of the uint16_t. + * @param[out] p_len By reference pointer to hold the length of the serialized value in the buffer. + * @param[in] value Value to convert serialize into a byte buffer. + * + * @retval NRF_SUCCESS If the conversion from uint16_t value to byte buffer was successful. + */ +void lwm2m_tlv_uint16_to_bytebuffer(uint8_t * p_buffer, uint8_t * p_len, uint16_t value); + +/**@brief Set a uint32_t value to a lwm2m_tlv_t structure. + * + * @param[out] p_tlv TLV containing the uint32_t tlv. + * @param[in] value Value to set. + * @param[in] id Resource Id associated with the value. + */ +void lwm2m_tlv_uint32_set(lwm2m_tlv_t * p_tlv, uint32_t value, uint16_t id); + + +/**@brief Set a uint16_t value to a lwm2m_tlv_t structure. + * + * @param[out] p_tlv TLV containing the uint16_t tlv. + * @param[in] value Value to set. + * @param[in] id Resource Id associated with the value. + */ +void lwm2m_tlv_uint16_set(lwm2m_tlv_t * p_tlv, uint16_t value, uint16_t id); + + +/**@brief Set a boolean value to a lwm2m_tlv_t structure. + * + * @param[out] p_tlv TLV containing the boolean tlv. + * @param[in] value Value to set. + * @param[in] id Resource Id associated with the value. + */ +void lwm2m_tlv_bool_set(lwm2m_tlv_t * p_tlv, bool value, uint16_t id); + +/**@brief Set a string value to a lwm2m_tlv_t structure. + * + * @param[out] p_tlv TLV containing the string tlv. + * @param[in] value Value to set. + * @param[in] id Resource Id associated with the value. + */ +void lwm2m_tlv_string_set(lwm2m_tlv_t * p_tlv, lwm2m_string_t string, uint16_t id); + +/**@brief Set a opaque value to a lwm2m_tlv_t structure. + * + * @param[out] p_tlv TLV containing the opaque tlv. + * @param[in] value Value to set. + * @param[in] id Resource Id associated with the value. + */ +void lwm2m_tlv_opaque_set(lwm2m_tlv_t * p_tlv, lwm2m_opaque_t opaque, uint16_t id); + +#ifdef __cplusplus +} +#endif + +#endif // LWM2M_TLV_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ble_ncfgs/ble_ncfgs.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ble_ncfgs/ble_ncfgs.c new file mode 100644 index 0000000..241ec48 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ble_ncfgs/ble_ncfgs.c @@ -0,0 +1,516 @@ +/** + * Copyright (c) 2015-2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifdef COMMISSIONING_ENABLED + +#include <string.h> +#include "ble_ncfgs.h" +#include "app_error.h" +#include "ble.h" +#include "nordic_common.h" + +/**@brief NCFGS database encapsulation. */ +typedef struct +{ + uint16_t service_handle; + ble_gatts_char_handles_t ssid_handles; + ble_gatts_char_handles_t keys_store_handles; + ble_gatts_char_handles_t ctrlp_handles; +} ble_database_t; + +static ble_ncfgs_state_t m_service_state = NCFGS_STATE_IDLE; /**< Module state value. */ +static ble_ncfgs_evt_handler_t m_app_evt_handler; /**< Parent module callback function. */ +static ble_database_t m_database; /**< GATT handles database. */ +static uint8_t m_ctrlp_value_buffer[NCFGS_CTRLP_VALUE_LEN]; /**< Stores received Control Point value before parsing. */ +static ble_ncfgs_data_t m_ncfgs_data; /**< Stores all values written by the peer device. */ + +#if NCFGS_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME ble_ncfgs + +#define NRF_LOG_LEVEL NCFGS_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR NCFGS_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR NCFGS_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define NCFGS_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define NCFGS_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define NCFGS_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define NCFGS_ENTRY() NCFGS_TRC(">> %s", __func__) +#define NCFGS_EXIT() NCFGS_TRC("<< %s", __func__) + +#else // NCFGS_CONFIG_LOG_ENABLED + +#define NCFGS_TRC(...) /**< Disables traces. */ +#define NCFGS_DUMP(...) /**< Disables dumping of octet streams. */ +#define NCFGS_ERR(...) /**< Disables error logs. */ + +#define NCFGS_ENTRY(...) +#define NCFGS_EXIT(...) + +#endif // NCFGS_CONFIG_LOG_ENABLED + +/**@brief Function for adding the SSID Store characteristic. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t add_ssid_characteristic(ble_uuid_t * p_srv_uuid) +{ + ble_gatts_char_md_t char_md; + ble_gatts_attr_t attr_char_value; + ble_uuid_t char_uuid; + ble_gatts_attr_md_t attr_md; + + memset(&char_md, 0x00, sizeof(char_md)); + + char_md.char_props.read = 1; + char_md.char_props.write = 1; + + memset(&attr_md, 0x00, sizeof(attr_md)); + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); + attr_md.wr_auth = 1; + attr_md.vloc = BLE_GATTS_VLOC_USER; + + memset(&attr_char_value, 0x00, sizeof(attr_char_value)); + + char_uuid.type = p_srv_uuid->type; + char_uuid.uuid = BLE_UUID_NCFGS_SSID_CHAR; + + attr_char_value.p_uuid = &char_uuid; + attr_char_value.p_attr_md = &attr_md; + attr_char_value.init_len = NCFGS_SSID_MAX_LEN; + attr_char_value.init_offs = 0; + attr_char_value.max_len = NCFGS_SSID_MAX_LEN; + attr_char_value.p_value = &m_ncfgs_data.ssid_from_router.ssid[0]; + + return sd_ble_gatts_characteristic_add(m_database.service_handle, + &char_md, + &attr_char_value, + &m_database.ssid_handles); +} + + +/**@brief Function for adding the Keys Store characteristic. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t add_keys_store_characteristic(ble_uuid_t * p_srv_uuid) +{ + ble_gatts_char_md_t char_md; + ble_gatts_attr_t attr_char_value; + ble_uuid_t char_uuid; + ble_gatts_attr_md_t attr_md; + + memset(&char_md, 0x00, sizeof(char_md)); + + char_md.char_props.read = 1; + char_md.char_props.write = 1; + + memset(&attr_md, 0x00, sizeof(attr_md)); + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); + attr_md.wr_auth = 1; + attr_md.vloc = BLE_GATTS_VLOC_USER; + + memset(&attr_char_value, 0x00, sizeof(attr_char_value)); + + char_uuid.type = p_srv_uuid->type; + char_uuid.uuid = BLE_UUID_NCFGS_KEYS_STORE_CHAR; + + attr_char_value.p_uuid = &char_uuid; + attr_char_value.p_attr_md = &attr_md; + attr_char_value.init_len = NCFGS_KEYS_MAX_LEN; + attr_char_value.init_offs = 0; + attr_char_value.max_len = NCFGS_KEYS_MAX_LEN; + attr_char_value.p_value = &m_ncfgs_data.keys_from_router.keys[0]; + + return sd_ble_gatts_characteristic_add(m_database.service_handle, + &char_md, + &attr_char_value, + &m_database.keys_store_handles); +} + + +/**@brief Function for adding the Control Point characteristic. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t add_ip_cfg_cp_characteristic(ble_uuid_t * p_srv_uuid) +{ + ble_gatts_char_md_t char_md; + ble_gatts_attr_t attr_char_value; + ble_uuid_t char_uuid; + ble_gatts_attr_md_t attr_md; + + memset(&char_md, 0x00, sizeof(char_md)); + + char_md.char_props.read = 1; + char_md.char_props.write = 1; + + memset(&attr_md, 0x00, sizeof(attr_md)); + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); + attr_md.wr_auth = 1; + attr_md.vloc = BLE_GATTS_VLOC_USER; + + memset(&attr_char_value, 0x00, sizeof(attr_char_value)); + + char_uuid.type = p_srv_uuid->type; + char_uuid.uuid = BLE_UUID_NCFGS_CTRLPT_CHAR; + + attr_char_value.p_uuid = &char_uuid; + attr_char_value.p_attr_md = &attr_md; + attr_char_value.init_len = NCFGS_CTRLP_VALUE_LEN; + attr_char_value.init_offs = 0; + attr_char_value.max_len = NCFGS_CTRLP_VALUE_LEN; + attr_char_value.p_value = &m_ctrlp_value_buffer[0]; + + return sd_ble_gatts_characteristic_add(m_database.service_handle, + &char_md, + &attr_char_value, + &m_database.ctrlp_handles); +} + + +/**@brief Function for creating the GATT database. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t ble_ncfgs_create_database(void) +{ + uint32_t err_code = NRF_SUCCESS; + + // Add service. + ble_uuid_t service_uuid; + + const ble_uuid128_t base_uuid128 = + { + { + 0x73, 0x3E, 0x2D, 0x02, 0xB7, 0x6B, 0xBE, 0xBE, \ + 0xE5, 0x4F, 0x40, 0x8F, 0x00, 0x00, 0x20, 0x54 + } + }; + + service_uuid.uuid = BLE_UUID_NODE_CFG_SERVICE; + + err_code = sd_ble_uuid_vs_add(&base_uuid128, &(service_uuid.type)); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, \ + &service_uuid, \ + &m_database.service_handle); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + err_code = add_ssid_characteristic(&service_uuid); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + err_code = add_keys_store_characteristic(&service_uuid); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + err_code = add_ip_cfg_cp_characteristic(&service_uuid); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + return err_code; +} + + +uint32_t ble_ncfgs_init(ble_ncfgs_evt_handler_t ble_ncfgs_cb) +{ + NCFGS_ENTRY(); + uint32_t err_code; + memset(&m_ncfgs_data, 0x00, sizeof(m_ncfgs_data)); + + m_app_evt_handler = ble_ncfgs_cb; + + err_code = ble_ncfgs_create_database(); + NCFGS_EXIT(); + return err_code; +} + + +/**@brief Function for decoding the Control Point characteristic value. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t ctrlp_value_decode(const ble_evt_t * p_ble_evt) +{ + uint16_t wr_req_value_len = \ + p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.len; + + memcpy(m_ctrlp_value_buffer, \ + p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.data, \ + wr_req_value_len); + + m_ncfgs_data.ctrlp_value.opcode = \ + (ble_ncfgs_opcode_t)m_ctrlp_value_buffer[0]; + memcpy((void *)&m_ncfgs_data.ctrlp_value.delay_sec, \ + &m_ctrlp_value_buffer[NCFGS_CTRLP_OPCODE_LEN], \ + sizeof(uint32_t)); + m_ncfgs_data.ctrlp_value.delay_sec = \ + HTONL(m_ncfgs_data.ctrlp_value.delay_sec); + memcpy((void *)&m_ncfgs_data.ctrlp_value.duration_sec, \ + &m_ctrlp_value_buffer[NCFGS_CTRLP_OPCODE_LEN+NCFGS_CTRLP_DELAY_LEN], \ + sizeof(uint32_t)); + m_ncfgs_data.ctrlp_value.duration_sec = \ + HTONL(m_ncfgs_data.ctrlp_value.duration_sec); + m_ncfgs_data.ctrlp_value.state_on_failure = \ + (state_on_failure_t)m_ctrlp_value_buffer[NCFGS_CTRLP_OPCODE_LEN+ \ + NCFGS_CTRLP_DELAY_LEN+ \ + NCFGS_CTRLP_DURATION_LEN]; + + if ((m_ncfgs_data.ctrlp_value.state_on_failure != NCFGS_SOF_NO_CHANGE) && \ + (m_ncfgs_data.ctrlp_value.state_on_failure != NCFGS_SOF_PWR_OFF) && \ + (m_ncfgs_data.ctrlp_value.state_on_failure != NCFGS_SOF_CONFIG_MODE)) + { + return NRF_ERROR_INVALID_DATA; + } + + uint16_t id_data_len = wr_req_value_len - NCFGS_CTRLP_ALL_BUT_ID_DATA_LEN; + if (id_data_len != 0) + { + m_ncfgs_data.id_data.identity_data_len = id_data_len; + + memcpy(m_ncfgs_data.id_data.identity_data, \ + &m_ctrlp_value_buffer[NCFGS_CTRLP_ALL_BUT_ID_DATA_LEN], \ + id_data_len); + } + + return NRF_SUCCESS; +} + + +void ble_ncfgs_ble_evt_handler(const ble_evt_t * p_ble_evt) +{ + switch (p_ble_evt->header.evt_id) + { + case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: + { + if (p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.op == \ + BLE_GATTS_OP_WRITE_REQ) + { + uint16_t wr_req_handle = \ + p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.handle; + uint16_t wr_req_value_len = \ + p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.len; + + ble_gatts_rw_authorize_reply_params_t reply_params; + memset(&reply_params, 0x00, sizeof(reply_params)); + + reply_params.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE; + reply_params.params.write.update = 1; + reply_params.params.write.offset = p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.offset; + reply_params.params.write.len = p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.len; + reply_params.params.write.p_data = p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.data; + + if (wr_req_handle == m_database.ssid_handles.value_handle) + { + NCFGS_TRC("> wr_req: ssid_handle"); + + if ((wr_req_value_len > NCFGS_SSID_MAX_LEN) || \ + (wr_req_value_len < NCFGS_SSID_MIN_LEN)) + { + reply_params.params.write.gatt_status = \ + BLE_GATT_STATUS_ATTERR_INVALID_ATT_VAL_LENGTH; + } + else + { + m_ncfgs_data.ssid_from_router.ssid_len = wr_req_value_len; + m_service_state |= NCFGS_STATE_SSID_WRITTEN; + + reply_params.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS; + } + + UNUSED_RETURN_VALUE( \ + sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gap_evt.conn_handle, \ + &reply_params)); + NCFGS_TRC("< wr_req: ssid_handle"); + return; + } + + else if (wr_req_handle == m_database.keys_store_handles.value_handle) + { + NCFGS_TRC("> wr_req: keys_store_handle"); + + if (wr_req_value_len > NCFGS_KEYS_MAX_LEN) + { + reply_params.params.write.gatt_status = \ + BLE_GATT_STATUS_ATTERR_INVALID_ATT_VAL_LENGTH; + } + else + { + m_ncfgs_data.keys_from_router.keys_len = wr_req_value_len; + m_service_state |= NCFGS_STATE_KEYS_STORE_WRITTEN; + reply_params.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS; + } + + UNUSED_RETURN_VALUE( \ + sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gap_evt.conn_handle, \ + &reply_params)); + NCFGS_TRC("< wr_req: keys_store_handle"); + return; + } + + else if (wr_req_handle == m_database.ctrlp_handles.value_handle) + { + NCFGS_TRC("> wr_req: ctrlp_handle"); + + bool notify_app = false; + + if ((wr_req_value_len > NCFGS_CTRLP_VALUE_LEN) || \ + (wr_req_value_len < NCFGS_CTRLP_ALL_BUT_ID_DATA_LEN)) + { + reply_params.params.write.gatt_status = \ + BLE_GATT_STATUS_ATTERR_INVALID_ATT_VAL_LENGTH; + } + else + { + ble_ncfgs_opcode_t opcode_in = (ble_ncfgs_opcode_t) \ + p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.data[0]; + + reply_params.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS; + + if ((opcode_in != NCFGS_OPCODE_GOTO_JOINING_MODE) && \ + (opcode_in != NCFGS_OPCODE_GOTO_CONFIG_MODE) && \ + (opcode_in != NCFGS_OPCODE_GOTO_IDENTITY_MODE)) + { + reply_params.params.write.gatt_status = APP_GATTERR_UNKNOWN_OPCODE; + } + + if (opcode_in == NCFGS_OPCODE_GOTO_JOINING_MODE) + { + if (!((m_service_state & NCFGS_STATE_SSID_WRITTEN) && \ + (m_service_state & NCFGS_STATE_KEYS_STORE_WRITTEN))) + { + reply_params.params.write.gatt_status = APP_GATTERR_NOT_CONFIGURED; + } + } + + if (reply_params.params.write.gatt_status == BLE_GATT_STATUS_SUCCESS) + { + uint32_t err_code = ctrlp_value_decode(p_ble_evt); + if (err_code != NRF_SUCCESS) + { + reply_params.params.write.gatt_status = \ + APP_GATTERR_INVALID_ATTR_VALUE; + } + else + { + notify_app = true; + } + } + } + + UNUSED_RETURN_VALUE(sd_ble_gatts_rw_authorize_reply( + p_ble_evt->evt.gap_evt.conn_handle, + &reply_params)); + + if (notify_app == true) + { + NCFGS_TRC("> do notify parent"); + + m_app_evt_handler(&m_ncfgs_data); + + NCFGS_TRC("< do notify parent"); + } + NCFGS_TRC("< wr_req: ctrlp_handle"); + } + else + { + // Invalid handle. + reply_params.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_INVALID_HANDLE; + UNUSED_RETURN_VALUE(sd_ble_gatts_rw_authorize_reply( + p_ble_evt->evt.gap_evt.conn_handle, &reply_params)); + } + } + + break; + } + + case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST: + { + ble_gap_data_length_params_t dl_params; + + // Clearing the struct will effectively set members to @ref BLE_GAP_DATA_LENGTH_AUTO. + memset(&dl_params, 0, sizeof(ble_gap_data_length_params_t)); + UNUSED_RETURN_VALUE(sd_ble_gap_data_length_update(p_ble_evt->evt.gap_evt.conn_handle, &dl_params, NULL)); + break; + } + + case BLE_GAP_EVT_PHY_UPDATE_REQUEST: + { + NCFGS_TRC("> PHY update request."); + + ble_gap_phys_t const phys = + { + .rx_phys = BLE_GAP_PHY_AUTO, + .tx_phys = BLE_GAP_PHY_AUTO, + }; + + UNUSED_RETURN_VALUE(sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys); + + NCFGS_TRC("< PHY update request."); + break; + } + + default: + { + break; + } + } +} + +#endif // COMMISSIONING_ENABLED diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ble_ncfgs/ble_ncfgs.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ble_ncfgs/ble_ncfgs.h new file mode 100644 index 0000000..b59495d --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ble_ncfgs/ble_ncfgs.h @@ -0,0 +1,194 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup ble_ncfgs Node Configuration Service + * @{ + * @ingroup iot_sdk_common + * @brief Node Configuration Service module. + * + * @details The Node Configuration Service allows configuration of the node during commissioning. + * During initialization it adds the Node Configuration Service and the corresponding + * characteristics to the BLE GATT database. It decodes and checks the received values + * and then passes them to the parent module. + */ + +#ifndef BLE_NODE_CFG_H__ +#define BLE_NODE_CFG_H__ + +#include <stdint.h> +#include "ble.h" +#include "app_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_UUID_NODE_CFG_SERVICE 0x7799 +#define BLE_UUID_NCFGS_SSID_CHAR 0x77A9 +#define BLE_UUID_NCFGS_KEYS_STORE_CHAR 0x77B9 +#define BLE_UUID_NCFGS_CTRLPT_CHAR 0x77C9 + +#define APP_GATTERR_NOT_CONFIGURED BLE_GATT_STATUS_ATTERR_APP_BEGIN + 1 /**< ATT Error: Node configuration incomplete. */ +#define APP_GATTERR_UNKNOWN_OPCODE BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2 /**< ATT Error: Unknown opcode. */ +#define APP_GATTERR_INVALID_ATTR_VALUE BLE_GATT_STATUS_ATTERR_APP_BEGIN + 3 /**< ATT Error: Invalid attribute value. */ + +#define NCFGS_SSID_MIN_LEN 6 /**< SSID minimum length. */ +#define NCFGS_SSID_MAX_LEN 16 /**< SSID maximum length. */ +#define NCFGS_KEYS_MAX_LEN 16 /**< Keys maximum length. */ +#define NCFGS_IDENTITY_DATA_MAX_LEN 8 /**< Identity data maximum length. */ + +#define NCFGS_CTRLP_OPCODE_LEN 1 /**< Ctrlp: Opcode value length. */ +#define NCFGS_CTRLP_DELAY_LEN 4 /**< Ctrlp: Length of action delay value. */ +#define NCFGS_CTRLP_DURATION_LEN 4 /**< Ctrlp: Length of next mode duration value. */ +#define NCFGS_CTRLP_STATE_ON_FAILURE_LEN 1 /**< Ctrlp: Length of state-on-failure value. */ + +#define NCFGS_CTRLP_ALL_BUT_ID_DATA_LEN (NCFGS_CTRLP_OPCODE_LEN + \ + NCFGS_CTRLP_DELAY_LEN + \ + NCFGS_CTRLP_DURATION_LEN + \ + NCFGS_CTRLP_STATE_ON_FAILURE_LEN) /**< Ctrlp: Total length of all values except identity data. */ +#define NCFGS_CTRLP_VALUE_LEN (NCFGS_CTRLP_OPCODE_LEN + \ + NCFGS_CTRLP_DELAY_LEN + \ + NCFGS_CTRLP_DURATION_LEN + \ + NCFGS_CTRLP_STATE_ON_FAILURE_LEN + \ + NCFGS_IDENTITY_DATA_MAX_LEN) /**< Ctrlp: Total length of all values. */ + +#define HTONL(val) ((((uint32_t) (val) & 0xff000000) >> 24) | \ + (((uint32_t) (val) & 0x00ff0000) >> 8) | \ + (((uint32_t) (val) & 0x0000ff00) << 8) | \ + (((uint32_t) (val) & 0x000000ff) << 24)) + +/**@brief Node Configuration Service control point opcode values. */ +typedef enum +{ + NCFGS_OPCODE_GOTO_JOINING_MODE = 0x01, + NCFGS_OPCODE_GOTO_CONFIG_MODE = 0x02, + NCFGS_OPCODE_GOTO_IDENTITY_MODE = 0x03 +} ble_ncfgs_opcode_t; + +/**@brief Node Configuration Service configuration states. */ +typedef enum +{ + NCFGS_STATE_IDLE = 0x00, + NCFGS_STATE_SSID_WRITTEN = 0x01, + NCFGS_STATE_KEYS_STORE_WRITTEN = 0x02 +} ble_ncfgs_state_t; + +/**@brief Node Configuration Service state-on-failure values. */ +typedef enum +{ + NCFGS_SOF_NO_CHANGE = 0x00, + NCFGS_SOF_PWR_OFF = 0x01, + NCFGS_SOF_CONFIG_MODE = 0x02 +} state_on_failure_t; + +/**@brief Structure for storing keys received from the peer. */ +typedef struct __attribute__ ((__packed__)) +{ + uint8_t keys_len; + uint8_t keys[NCFGS_KEYS_MAX_LEN]; // Keys received from the router. +} keys_store_t; + +/**@brief Structure for storing the SSID received from the peer. */ +typedef struct __attribute__ ((__packed__)) +{ + uint8_t ssid_len; + uint8_t ssid[NCFGS_SSID_MAX_LEN]; // SSID received from the router. +} ssid_store_t; + +/**@brief Structure for storing the identity data from the peer. */ +typedef struct __attribute__ ((__packed__)) +{ + uint8_t identity_data_len; + uint8_t identity_data[NCFGS_IDENTITY_DATA_MAX_LEN]; // Custom node identifier data. +} id_data_store_t; + +/**@brief Structure for control point value. */ +typedef struct __attribute__ ((__packed__)) +{ + ble_ncfgs_opcode_t opcode; // Mode to start. + uint32_t delay_sec; // Delay before entering >Opcode< mode. + uint32_t duration_sec; // General timeout for >Opcode< mode. + state_on_failure_t state_on_failure; // Mode to enter if >Opcode< mode fails (times out). +} ble_ncfgs_ctrlp_value_t; + +/**@brief Structure for storing Node Configuration Service characteristic values. */ +typedef struct __attribute__ ((__packed__)) +{ + ble_ncfgs_ctrlp_value_t ctrlp_value; + ssid_store_t ssid_from_router; // SSID received from the peer. + keys_store_t keys_from_router; // Keys received from the peer. + id_data_store_t id_data; // Identity data received from the peer. +} ble_ncfgs_data_t; + +/**@brief Function for handling BLE events. + * + * @details This function must be called from the BLE stack event dispatcher + * to handle BLE events that are relevant for the Node Configuration Service module. + * It propagates an event to the parent layer if the Control Point characteristic + * was successfully written. + * + * @param[in] p_ble_evt BLE stack event. + */ +void ble_ncfgs_ble_evt_handler(const ble_evt_t * p_ble_evt); + +/**@brief Node Configuration Service event handler type. */ +typedef void (*ble_ncfgs_evt_handler_t) (ble_ncfgs_data_t * ncfgs_data); + +/**@brief Function for initializing the Node Configuration Service module. + * + * @details Interface for the Commissioning module to create a GATT database to + * allow for node configuration. + * + * @param[in] ble_ncfgs_cb Function to be called in case of an error. + * + * @retval NRF_SUCCESS If initialization was successful. Otherwise, a propagated + * error code is returned. + * + */ +uint32_t ble_ncfgs_init(ble_ncfgs_evt_handler_t ble_ncfgs_cb); + +#ifdef __cplusplus +} +#endif + +#endif // BLE_NODE_CFG_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/commissioning/commissioning.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/commissioning/commissioning.c new file mode 100644 index 0000000..4ea6884 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/commissioning/commissioning.c @@ -0,0 +1,1077 @@ +/** + * Copyright (c) 2015-2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifdef COMMISSIONING_ENABLED + +#include <string.h> +#include "boards.h" +#include "ble_hci.h" +#include "nrf_soc.h" +#include "app_error.h" +#include "fds.h" +#include "ble_advdata.h" +#include "commissioning.h" +#include "nordic_common.h" +#include "ble_srv_common.h" +#include "sdk_config.h" + +#define MINIMUM_ACTION_DELAY 2 /**< Delay before executing an action after the control point was written (in seconds). */ + +#define SEC_PARAM_BOND 0 /**< Perform bonding. */ +#define SEC_PARAM_MITM 1 /**< Man In The Middle protection required (applicable when display module is detected). */ +#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_KEYBOARD_ONLY /**< Display I/O capabilities. */ +#define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */ +#define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */ +#define SEC_PARAM_MAX_KEY_SIZE 16 /**< Maximum encryption key size. */ + +#define COMM_FDS_FILE_ID 0xCAFE /**< The ID of the file that the record belongs to. */ +#define COMM_FDS_RECORD_KEY 0xBEAF /**< The record key of FDS record that keeps node settings. */ + +#define NUMBER_OF_COMMISSIONING_TIMERS 4 +#define TIMER_INDEX_DELAYED_ACTION 0 +#define TIMER_INDEX_CONFIG_MODE 1 +#define TIMER_INDEX_JOINING_MODE 2 +#define TIMER_INDEX_IDENTITY_MODE 3 + +#define SEC_TO_MILLISEC(PARAM) (PARAM * 1000) + +static commissioning_settings_t m_node_settings; /**< All node settings as configured through the Node Configuration Service. */ +static commissioning_evt_handler_t m_commissioning_evt_handler; /**< Commissioning event handler of the parent layer. */ +static bool m_power_off_on_failure = false; /**< Power off on failure setting from the last NCFGS event. */ +static commissioning_timer_t m_commissioning_timers[NUMBER_OF_COMMISSIONING_TIMERS]; + +static ipv6_medium_ble_gap_params_t m_config_mode_gap_params; /**< Advertising parameters in Config mode. */ +static ipv6_medium_ble_adv_params_t m_config_mode_adv_params; /**< GAP parameters in Config mode. */ + +static ipv6_medium_ble_gap_params_t m_joining_mode_gap_params; /**< Advertising parameters in Joining mode. */ +static ipv6_medium_ble_adv_params_t m_joining_mode_adv_params; /**< GAP parameters in Joining mode. */ + +static ble_uuid_t m_config_mode_adv_uuids[] = \ + { + {BLE_UUID_NODE_CFG_SERVICE, \ + BLE_UUID_TYPE_VENDOR_BEGIN} + }; /**< Config mode: List of available service UUIDs in advertisement data. */ + +static ble_uuid_t m_joining_mode_adv_uuids[] = \ + { + {BLE_UUID_IPSP_SERVICE, BLE_UUID_TYPE_BLE} + }; /**< Joining mode: List of available service UUIDs in advertisement data. */ + +static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the active connection. */ +static uint8_t m_current_mode = NODE_MODE_NONE; /**< Current mode value. */ +static uint8_t m_next_mode = NODE_MODE_NONE; /**< Value of the mode the node will enter when the timeout handler of m_delayed_action_timer is triggered. */ + +#if (FDS_ENABLED == 1) +static fds_record_desc_t m_fds_record_desc; /**< Descriptor of FDS record. */ +#endif + +#define COMM_ENABLE_LOGS 1 /**< Set to 0 to disable debug trace in the module. */ + +#if COMMISSIONING_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME commissioning + +#define NRF_LOG_LEVEL COMMISSIONING_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR COMMISSIONING_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR COMMISSIONING_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define COMM_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define COMM_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define COMM_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define COMM_ENTRY() COMM_TRC(">> %s", __func__) +#define COMM_EXIT() COMM_TRC("<< %s", __func__) + +#else // COMMISSIONING_CONFIG_LOG_ENABLED + +#define COMM_TRC(...) /**< Disables traces. */ +#define COMM_DUMP(...) /**< Disables dumping of octet streams. */ +#define COMM_ERR(...) /**< Disables error logs. */ + +#define COMM_ENTRY(...) +#define COMM_EXIT(...) + +#endif // COMMISSIONING_CONFIG_LOG_ENABLED + + +/**@brief Function for validating all node settings. + */ +static bool settings_are_valid() +{ + uint8_t tmp = m_node_settings.poweron_mode; + if (tmp == 0xFF) + { + return false; + } + else + { + return true; + } +} + +#if (FDS_ENABLED == 1) +/**@brief Function for updating the node settings in persistent memory. + */ +static uint32_t persistent_settings_update(void) +{ + uint32_t err_code; + + fds_find_token_t token; + memset(&token, 0, sizeof(token)); + + fds_record_t record; + memset(&record, 0, sizeof(record)); + + record.file_id = COMM_FDS_FILE_ID; + record.key = COMM_FDS_RECORD_KEY; + record.data.p_data = &m_node_settings; + record.data.length_words = ALIGN_NUM(4, sizeof(commissioning_settings_t))/sizeof(uint32_t); + + // Try to find FDS record with node settings. + err_code = fds_record_find(COMM_FDS_FILE_ID, COMM_FDS_RECORD_KEY, &m_fds_record_desc, &token); + if (err_code == FDS_SUCCESS) + { + err_code = fds_record_update(&m_fds_record_desc, &record); + } + else + { + + err_code = fds_record_write(&m_fds_record_desc, &record); + } + + if (err_code == FDS_ERR_NO_SPACE_IN_FLASH) + { + // Run garbage collector to reclaim the flash space that is occupied by records that have been deleted, + // or that failed to be completely written due to, for example, a power loss. + err_code = fds_gc(); + } + + return err_code; +} + + +/**@brief Function for loading node settings from the persistent memory. + */ +static void persistent_settings_load(void) +{ + uint32_t err_code = FDS_SUCCESS; + fds_flash_record_t record; + + fds_find_token_t token; + memset(&token, 0, sizeof(token)); + + // Try to find FDS record with node settings. + err_code = fds_record_find(COMM_FDS_FILE_ID, COMM_FDS_RECORD_KEY, &m_fds_record_desc, &token); + if (err_code == FDS_SUCCESS) + { + err_code = fds_record_open(&m_fds_record_desc, &record); + if (err_code == FDS_SUCCESS) + { + if (record.p_data) + { + memcpy(&m_node_settings, record.p_data, sizeof(m_node_settings)); + } + } + } +} + + +/**@brief Function for clearing node settings from the persistent memory. + */ +static void persistent_settings_clear(void) +{ + fds_record_delete(&m_fds_record_desc); +} + +/**@brief Function for handling File Data Storage events. + */ +static void persistent_settings_cb(fds_evt_t const * p_evt) +{ + if (p_evt->id == FDS_EVT_GC) + { + if (settings_are_valid()) + { + persistent_settings_update(); + } + } +} + + +/**@brief Function for initializing the File Data Storage module. + */ +static uint32_t persistent_settings_init(void) +{ + uint32_t err_code; + + err_code = fds_init(); + if (err_code == FDS_SUCCESS) + { + err_code = fds_register(persistent_settings_cb); + } + + return err_code; +} +#endif + + +/**@brief Function for setting advertisement parameters in Config mode. + */ +static void config_mode_adv_params_set(void) +{ + COMM_ENTRY(); + memset(&m_config_mode_adv_params, 0x00, sizeof(m_config_mode_adv_params)); + + m_config_mode_adv_params.advdata.name_type = BLE_ADVDATA_FULL_NAME; + m_config_mode_adv_params.advdata.include_appearance = false; + m_config_mode_adv_params.advdata.flags = \ + BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; + m_config_mode_adv_params.advdata.uuids_complete.uuid_cnt = \ + sizeof(m_config_mode_adv_uuids) / sizeof(m_config_mode_adv_uuids[0]); + m_config_mode_adv_params.advdata.uuids_complete.p_uuids = m_config_mode_adv_uuids; + m_config_mode_adv_params.advdata.p_manuf_specific_data = NULL; + + if (m_node_settings.id_data_store.identity_data_len > 0) + { + m_config_mode_adv_params.sr_man_specific_data.data.size = \ + m_node_settings.id_data_store.identity_data_len; + m_config_mode_adv_params.sr_man_specific_data.data.p_data = \ + m_node_settings.id_data_store.identity_data; + m_config_mode_adv_params.sr_man_specific_data.company_identifier = \ + COMPANY_IDENTIFIER; + m_config_mode_adv_params.srdata.p_manuf_specific_data = \ + &m_config_mode_adv_params.sr_man_specific_data; + } + else + { + m_config_mode_adv_params.srdata.p_manuf_specific_data = NULL; + } + + m_config_mode_adv_params.advparams.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; + m_config_mode_adv_params.advparams.p_peer_addr = NULL; // Undirected advertisement. + m_config_mode_adv_params.advparams.filter_policy = BLE_GAP_ADV_FP_ANY; + m_config_mode_adv_params.advparams.interval = CONFIG_MODE_ADV_ADV_INTERVAL; + m_config_mode_adv_params.advparams.duration = CONFIG_MODE_ADV_TIMEOUT; + + COMM_EXIT(); +} + + +/**@brief Function for setting GAP parameters in Config mode. + */ +static void config_mode_gap_params_set(void) +{ + COMM_ENTRY(); + + memset(&m_config_mode_gap_params, 0x00, sizeof(m_config_mode_gap_params)); + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&m_config_mode_gap_params.sec_mode); + + m_config_mode_gap_params.p_dev_name = (const uint8_t *)CONFIG_MODE_DEVICE_NAME; + m_config_mode_gap_params.dev_name_len = strlen(CONFIG_MODE_DEVICE_NAME); + + m_config_mode_gap_params.gap_conn_params.min_conn_interval = \ + (uint16_t)CONFIG_MODE_MIN_CONN_INTERVAL; + m_config_mode_gap_params.gap_conn_params.max_conn_interval = \ + (uint16_t)CONFIG_MODE_MAX_CONN_INTERVAL; + m_config_mode_gap_params.gap_conn_params.slave_latency = CONFIG_MODE_SLAVE_LATENCY; + m_config_mode_gap_params.gap_conn_params.conn_sup_timeout = CONFIG_MODE_CONN_SUP_TIMEOUT; + + COMM_EXIT(); +} + + +/**@brief Function for setting advertisement parameters in Joining mode. + */ +static void joining_mode_adv_params_set(void) +{ + COMM_ENTRY(); + + memset(&m_joining_mode_adv_params, 0x00, sizeof(m_joining_mode_adv_params)); + + if (m_node_settings.ssid_store.ssid_len > 0) + { + m_joining_mode_adv_params.adv_man_specific_data.data.size = \ + m_node_settings.ssid_store.ssid_len; + m_joining_mode_adv_params.adv_man_specific_data.data.p_data = \ + m_node_settings.ssid_store.ssid; + m_joining_mode_adv_params.adv_man_specific_data.company_identifier = \ + COMPANY_IDENTIFIER; + } + + m_joining_mode_adv_params.advdata.name_type = BLE_ADVDATA_NO_NAME; + m_joining_mode_adv_params.advdata.include_appearance = false; + m_joining_mode_adv_params.advdata.flags = \ + BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED; + m_joining_mode_adv_params.advdata.uuids_complete.uuid_cnt = \ + sizeof(m_joining_mode_adv_uuids) / sizeof(m_joining_mode_adv_uuids[0]); + m_joining_mode_adv_params.advdata.uuids_complete.p_uuids = m_joining_mode_adv_uuids; + if (m_node_settings.ssid_store.ssid_len > 0) + { + m_joining_mode_adv_params.advdata.p_manuf_specific_data = \ + &m_joining_mode_adv_params.adv_man_specific_data; + } + else + { + m_joining_mode_adv_params.advdata.p_manuf_specific_data = NULL; + } + + if (m_node_settings.id_data_store.identity_data_len > 0) + { + m_joining_mode_adv_params.sr_man_specific_data.data.size = \ + m_node_settings.id_data_store.identity_data_len; + m_joining_mode_adv_params.sr_man_specific_data.data.p_data = \ + m_node_settings.id_data_store.identity_data; + m_joining_mode_adv_params.sr_man_specific_data.company_identifier = \ + COMPANY_IDENTIFIER; + m_joining_mode_adv_params.srdata.p_manuf_specific_data = \ + &m_joining_mode_adv_params.sr_man_specific_data; + } + else + { + m_joining_mode_adv_params.srdata.p_manuf_specific_data = NULL; + } + + m_joining_mode_adv_params.advparams.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; + m_joining_mode_adv_params.advparams.p_peer_addr = NULL; // Undirected advertisement. + m_joining_mode_adv_params.advparams.filter_policy = BLE_GAP_ADV_FP_ANY; + m_joining_mode_adv_params.advparams.interval = APP_ADV_ADV_INTERVAL; + m_joining_mode_adv_params.advparams.duration = APP_ADV_DURATION; + + COMM_EXIT(); +} + + +/**@brief Function for setting GAP parameters in Joining mode. + */ +static void joining_mode_gap_params_set(void) +{ + COMM_ENTRY(); + + memset(&m_joining_mode_gap_params, 0x00, sizeof(m_joining_mode_gap_params)); + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&m_joining_mode_gap_params.sec_mode); + + m_joining_mode_gap_params.appearance = BLE_APPEARANCE_UNKNOWN; + + m_joining_mode_gap_params.p_dev_name = (const uint8_t *)DEVICE_NAME; + m_joining_mode_gap_params.dev_name_len = strlen(DEVICE_NAME); + + m_joining_mode_gap_params.gap_conn_params.min_conn_interval = \ + (uint16_t)JOINING_MODE_MIN_CONN_INTERVAL; + m_joining_mode_gap_params.gap_conn_params.max_conn_interval = \ + (uint16_t)JOINING_MODE_MAX_CONN_INTERVAL; + m_joining_mode_gap_params.gap_conn_params.slave_latency = JOINING_MODE_SLAVE_LATENCY; + m_joining_mode_gap_params.gap_conn_params.conn_sup_timeout = JOINING_MODE_CONN_SUP_TIMEOUT; + + COMM_EXIT(); +} + + +/**@brief Function for starting a timer in the Commissioning module. + * + */ +static void commissioning_timer_start(uint8_t index, uint32_t timeout_sec) +{ + m_commissioning_timers[index].is_timer_running = true; + m_commissioning_timers[index].current_value_sec = timeout_sec; +} + + +/**@brief Function for stopping and re-setting a timer in the Commissioning module. + * + */ +static void commissioning_timer_stop_reset(uint8_t index) +{ + m_commissioning_timers[index].is_timer_running = false; + m_commissioning_timers[index].current_value_sec = 0x00; +} + + +void commissioning_node_mode_change(uint8_t new_mode) +{ + COMM_ENTRY(); + + commissioning_evt_t commissioning_evt; + memset(&commissioning_evt, 0x00, sizeof(commissioning_evt)); + commissioning_evt.p_commissioning_settings = &m_node_settings; + commissioning_evt.power_off_enable_requested = m_power_off_on_failure; + + commissioning_timer_stop_reset(TIMER_INDEX_DELAYED_ACTION); + commissioning_timer_stop_reset(TIMER_INDEX_CONFIG_MODE); + commissioning_timer_stop_reset(TIMER_INDEX_JOINING_MODE); + + config_mode_gap_params_set(); + config_mode_adv_params_set(); + joining_mode_gap_params_set(); + joining_mode_adv_params_set(); + + m_current_mode = new_mode; + + switch (m_current_mode) + { + case NODE_MODE_CONFIG: + { + commissioning_evt.commissioning_evt_id = COMMISSIONING_EVT_CONFIG_MODE_ENTER; + m_commissioning_evt_handler(&commissioning_evt); + + // Start Configuration mode timer. + COMM_TRC("Config mode timeout: %ld seconds", m_node_settings.config_mode_to); + commissioning_timer_start(TIMER_INDEX_CONFIG_MODE, m_node_settings.config_mode_to); + + break; + } + case NODE_MODE_JOINING: + { + commissioning_evt.commissioning_evt_id = COMMISSIONING_EVT_JOINING_MODE_ENTER; + m_commissioning_evt_handler(&commissioning_evt); + + // Start Joining mode timer. + COMM_TRC("Joining mode timeout: %ld seconds", m_node_settings.joining_mode_to); + commissioning_timer_start(TIMER_INDEX_JOINING_MODE, m_node_settings.joining_mode_to); + + break; + } + case NODE_MODE_IDENTITY: + { + commissioning_evt.commissioning_evt_id = COMMISSIONING_EVT_IDENTITY_MODE_ENTER; + m_commissioning_evt_handler(&commissioning_evt); + + // Start Identity mode timer. + COMM_TRC("Identity mode timeout: %ld seconds", m_node_settings.id_mode_to); + commissioning_timer_start(TIMER_INDEX_IDENTITY_MODE, m_node_settings.id_mode_to); + + break; + } + default: + { + break; + } + } + + COMM_EXIT(); +} + + +/**@brief Function for handling the Delayed action timer timeout. + * + * @details This function will be called each time the delayed action timer expires. + * + */ +static void action_timeout_handler(void) +{ + COMM_ENTRY(); + + commissioning_node_mode_change(m_next_mode); + + COMM_EXIT(); +} + + +/**@brief Function for handling the Config mode timer timeout. + * + * @details This function will be called each time the Config mode timer expires. + * + */ +static void config_mode_timeout_handler(void) +{ + COMM_ENTRY(); + + switch (m_node_settings.config_mode_failure) + { + case NCFGS_SOF_NO_CHANGE: + // Fall-through. + case NCFGS_SOF_CONFIG_MODE: + { + commissioning_node_mode_change(NODE_MODE_CONFIG); + + break; + } + case NCFGS_SOF_PWR_OFF: + { + LEDS_OFF(LEDS_MASK); + // The main timer in Config mode timed out, power off. + UNUSED_VARIABLE(sd_power_system_off()); + + break; + } + } + + COMM_EXIT(); +} + + +/**@brief Function for handling the Joining mode timer timeout. + * + * @details This function will be called each time the Joining mode timer expires. + * + */ +void joining_mode_timeout_handler(void) +{ + COMM_ENTRY(); + + switch (m_node_settings.joining_mode_failure) + { + case NCFGS_SOF_NO_CHANGE: + { + commissioning_node_mode_change(NODE_MODE_JOINING); + break; + } + case NCFGS_SOF_PWR_OFF: + { + LEDS_OFF(LEDS_MASK); + + UNUSED_VARIABLE(sd_power_system_off()); + break; + } + case NCFGS_SOF_CONFIG_MODE: + { + commissioning_node_mode_change(NODE_MODE_CONFIG); + break; + } + } + + COMM_EXIT(); +} + + +/**@brief Function for handling the Identity mode timer timeout. + * + * @details This function will be called each time the Identity mode timer expires. + * + */ +void identity_mode_timeout_handler(void) +{ + COMM_ENTRY(); + + commissioning_evt_t commissioning_evt; + memset(&commissioning_evt, 0x00, sizeof(commissioning_evt)); + commissioning_evt.commissioning_evt_id = COMMISSIONING_EVT_IDENTITY_MODE_EXIT; + + m_commissioning_evt_handler(&commissioning_evt); + + COMM_EXIT(); +} + + +void commissioning_joining_mode_timer_ctrl( \ + joining_mode_timer_ctrl_cmd_t joining_mode_timer_ctrl_cmd) +{ + switch (joining_mode_timer_ctrl_cmd) + { + case JOINING_MODE_TIMER_STOP_RESET: + { + commissioning_timer_stop_reset(TIMER_INDEX_JOINING_MODE); + + break; + } + case JOINING_MODE_TIMER_START: + { + commissioning_timer_start(TIMER_INDEX_JOINING_MODE, m_node_settings.joining_mode_to); + + break; + } + } +} + + +void commissioning_gap_params_get(ipv6_medium_ble_gap_params_t ** pp_node_gap_params) +{ + switch (m_current_mode) + { + case NODE_MODE_JOINING: + { + *pp_node_gap_params = &m_joining_mode_gap_params; + + break; + } + case NODE_MODE_IDENTITY: + // Fall-through. + case NODE_MODE_CONFIG: + { + *pp_node_gap_params = &m_config_mode_gap_params; + + break; + } + } +} + + +void commissioning_adv_params_get(ipv6_medium_ble_adv_params_t ** pp_node_adv_params) +{ + switch (m_current_mode) + { + case NODE_MODE_JOINING: + { + *pp_node_adv_params = &m_joining_mode_adv_params; + + break; + } + case NODE_MODE_IDENTITY: + // Fall-through. + case NODE_MODE_CONFIG: + { + *pp_node_adv_params = &m_config_mode_adv_params; + + break; + } + } +} + + +/**@brief Function for reading all node settings from the persistent storage. + */ +static void read_node_settings(void) +{ + memset(&m_node_settings, 0x00, sizeof(m_node_settings)); + +#if (FDS_ENABLED == 1) + persistent_settings_load(); +#endif // FDS_ENABLED + + if (m_node_settings.ssid_store.ssid_len > NCFGS_SSID_MAX_LEN) + { + m_node_settings.ssid_store.ssid_len = 0; + } + if (m_node_settings.keys_store.keys_len > NCFGS_KEYS_MAX_LEN) + { + m_node_settings.keys_store.keys_len = 0; + } + if (m_node_settings.id_data_store.identity_data_len > NCFGS_IDENTITY_DATA_MAX_LEN) + { + m_node_settings.id_data_store.identity_data_len = 0; + } + + // The duration of each mode needs to be at least 10 second. + m_node_settings.joining_mode_to = \ + (m_node_settings.joining_mode_to < 10) ? 10 : m_node_settings.joining_mode_to; + m_node_settings.config_mode_to = \ + (m_node_settings.config_mode_to < 10) ? 10 : m_node_settings.config_mode_to; + m_node_settings.id_mode_to = \ + (m_node_settings.id_mode_to < 10) ? 10 : m_node_settings.id_mode_to; +} + +#if (COMM_ENABLE_LOGS == 1) +/**@brief Function for printing all node settings. + */ +static void print_node_settings(void) +{ + COMM_TRC(""); + COMM_TRC(" Commissioning settings in memory:"); + COMM_TRC(" Start mode: %5d", m_node_settings.poweron_mode); + COMM_TRC(" Mode if Joining Mode fails: %5d", m_node_settings.joining_mode_failure); + COMM_TRC(" General timeout in Joining Mode: %5ld", m_node_settings.joining_mode_to); + COMM_TRC(" Mode if Configuration Mode fails: %5d", m_node_settings.config_mode_failure); + COMM_TRC("General timeout in Configuration Mode: %5ld", m_node_settings.config_mode_to); + COMM_TRC(" Identity Mode duration: %5ld", m_node_settings.id_mode_to); + COMM_TRC(" Stored Keys length: %5d", m_node_settings.keys_store.keys_len); + COMM_TRC(" Stored Keys:"); + uint8_t ii; + for (ii=0; ii<m_node_settings.keys_store.keys_len; ++ii) + { + COMM_TRC("0x%02X", m_node_settings.keys_store.keys[ii]); + } + COMM_TRC(""); + COMM_TRC(" Stored SSID length: %5d", m_node_settings.ssid_store.ssid_len); + COMM_TRC(" Stored SSID:"); + for (ii=0; ii<m_node_settings.ssid_store.ssid_len; ++ii) + { + COMM_TRC("0x%02X", m_node_settings.ssid_store.ssid[ii]); + } + COMM_TRC(""); + COMM_TRC(" Stored Identity Data length: %5d", m_node_settings.id_data_store.identity_data_len); + COMM_TRC(" Stored Identity Data:"); + for (ii=0; ii<m_node_settings.id_data_store.identity_data_len; ++ii) + { + COMM_TRC("0x%02X", m_node_settings.id_data_store.identity_data[ii]); + } + COMM_TRC(""); +} +#endif // (COMM_ENABLE_LOGS == 1) + + +void commissioning_settings_clear(void) +{ + COMM_ENTRY(); + memset(&m_node_settings, 0x00, sizeof(m_node_settings)); + +#if (FDS_ENABLED == 1) + persistent_settings_clear(); +#endif // FDS_ENABLED + + COMM_EXIT(); +} + + +void commissioning_ble_evt_handler(const ble_evt_t * p_ble_evt) +{ + uint32_t err_code; + + switch (p_ble_evt->header.evt_id) + { + case BLE_GAP_EVT_CONNECTED: + { + m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + commissioning_timer_stop_reset(TIMER_INDEX_DELAYED_ACTION); + commissioning_timer_stop_reset(TIMER_INDEX_CONFIG_MODE); + + break; + } + case BLE_GAP_EVT_DISCONNECTED: + { + m_conn_handle = BLE_CONN_HANDLE_INVALID; + if (m_current_mode == NODE_MODE_CONFIG) + { + commissioning_timer_start(TIMER_INDEX_CONFIG_MODE, \ + m_node_settings.config_mode_to); + } + if (m_current_mode == NODE_MODE_JOINING) + { + commissioning_timer_start(TIMER_INDEX_JOINING_MODE, \ + m_node_settings.joining_mode_to); + } + + break; + } + case BLE_GAP_EVT_AUTH_KEY_REQUEST: + { + if (m_current_mode == NODE_MODE_JOINING) + { + // If passkey is shorter than BLE_GAP_PASSKEY_LEN, add '0' character. + if (m_node_settings.keys_store.keys_len < BLE_GAP_PASSKEY_LEN) + { + memset(&m_node_settings.keys_store.keys[m_node_settings.keys_store.keys_len], \ + '0', BLE_GAP_PASSKEY_LEN - m_node_settings.keys_store.keys_len); + } + + // Short passkey to 6-length character. + m_node_settings.keys_store.keys[BLE_GAP_PASSKEY_LEN] = 0; + + COMM_TRC("Stored passkey is: %s", m_node_settings.keys_store.keys); + + err_code = sd_ble_gap_auth_key_reply(m_conn_handle, \ + BLE_GAP_AUTH_KEY_TYPE_PASSKEY, \ + m_node_settings.keys_store.keys); + APP_ERROR_CHECK(err_code); + } + + break; + } + case BLE_GAP_EVT_AUTH_STATUS: + { + if (m_current_mode == NODE_MODE_JOINING) + { + COMM_TRC("Status of authentication: %08x", \ + p_ble_evt->evt.gap_evt.params.auth_status.auth_status); + } + + break; + } + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + { + if (m_current_mode == NODE_MODE_JOINING) + { + ble_gap_sec_params_t sec_param; + ble_gap_sec_keyset_t keys_exchanged; + + memset(&sec_param, 0, sizeof(ble_gap_sec_params_t)); + memset(&keys_exchanged, 0, sizeof(ble_gap_sec_keyset_t)); + + sec_param.bond = SEC_PARAM_BOND; + sec_param.oob = SEC_PARAM_OOB; + sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE; + sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE; + sec_param.mitm = SEC_PARAM_MITM; + sec_param.io_caps = SEC_PARAM_IO_CAPABILITIES; + + err_code = sd_ble_gap_sec_params_reply(p_ble_evt->evt.gap_evt.conn_handle, + BLE_GAP_SEC_STATUS_SUCCESS, + &sec_param, + &keys_exchanged); + APP_ERROR_CHECK(err_code); + } + + break; + } + default: + { + break; + } + } +} + + +void on_ble_ncfgs_evt(ble_ncfgs_data_t * ncfgs_data) +{ + COMM_ENTRY(); + + commissioning_timer_stop_reset(TIMER_INDEX_DELAYED_ACTION); + commissioning_timer_stop_reset(TIMER_INDEX_CONFIG_MODE); + + uint32_t mode_duration_sec; + mode_duration_sec = ncfgs_data->ctrlp_value.duration_sec; + mode_duration_sec = (mode_duration_sec == 0) ? 1 : mode_duration_sec; + + switch (ncfgs_data->ctrlp_value.opcode) + { + case NCFGS_OPCODE_GOTO_JOINING_MODE: + { + m_next_mode = NODE_MODE_JOINING; + + m_node_settings.joining_mode_to = mode_duration_sec; + m_node_settings.joining_mode_failure = ncfgs_data->ctrlp_value.state_on_failure; + + /* This code will get executed in two scenarios: + - if the previous mode was Config mode and now we are ready to connect to the router, or + - if the previous mode was Joining mode and the state on failure was set to No Change. + */ + if (m_node_settings.joining_mode_failure == NCFGS_SOF_NO_CHANGE) + { + m_node_settings.poweron_mode = NODE_MODE_JOINING; + } + else + { + // If the state on failure is NOT No Change, start next time in Config mode. + m_node_settings.poweron_mode = NODE_MODE_CONFIG; + } + + if (m_node_settings.joining_mode_failure == NCFGS_SOF_PWR_OFF) + { + COMM_TRC("Will power off on failure."); + m_power_off_on_failure = true; // The assert handler will power off the system. + } + + break; + } + case NCFGS_OPCODE_GOTO_CONFIG_MODE: + { + m_next_mode = NODE_MODE_CONFIG; + + m_node_settings.config_mode_to = mode_duration_sec; + m_node_settings.config_mode_failure = ncfgs_data->ctrlp_value.state_on_failure; + + /* The node is about to enter Config mode. Regardless of what the state on failure + setting is (No Change or Pwr Off or Cfg Mode), the poweron_mode value should be Cfg Mode. */ + m_node_settings.poweron_mode = NODE_MODE_CONFIG; + + if (m_node_settings.config_mode_failure == NCFGS_SOF_PWR_OFF) + { + COMM_TRC("Will power off on failure."); + m_power_off_on_failure = true; // The assert handler will power off the system. + } + + break; + } + case NCFGS_OPCODE_GOTO_IDENTITY_MODE: + { + m_next_mode = NODE_MODE_IDENTITY; + + m_node_settings.id_mode_to = mode_duration_sec; + + break; + } + default: + { + break; + } + } + + memcpy(&m_node_settings.ssid_store, &ncfgs_data->ssid_from_router, sizeof(ssid_store_t)); + memcpy(&m_node_settings.keys_store, &ncfgs_data->keys_from_router, sizeof(keys_store_t)); + memcpy(&m_node_settings.id_data_store, &ncfgs_data->id_data, sizeof(id_data_store_t)); + +#if (COMM_ENABLE_LOGS == 1) + print_node_settings(); +#endif // (COMM_ENABLE_LOGS == 1) + +#if (FDS_ENABLED == 1) + uint32_t err_code = persistent_settings_update(); + APP_ERROR_CHECK(err_code); +#endif // FDS_ENABLED + + uint32_t action_delay_written = ncfgs_data->ctrlp_value.delay_sec; + // Set the timeout value to at least MINIMUM_ACTION_DELAY second(s). + // This is to make sure that storing settings in the persistent + // storage completes before activating the next mode. + action_delay_written = (action_delay_written < MINIMUM_ACTION_DELAY) ? \ + MINIMUM_ACTION_DELAY : action_delay_written; + + COMM_TRC("Action delay: %ld seconds.", action_delay_written); + commissioning_timer_start(TIMER_INDEX_DELAYED_ACTION, action_delay_written); + + COMM_EXIT(); +} + + +void commissioning_time_tick(iot_timer_time_in_ms_t wall_clock_value) +{ + UNUSED_PARAMETER(wall_clock_value); + uint8_t index; + + for (index=0; index<NUMBER_OF_COMMISSIONING_TIMERS; ++index) + { + if (m_commissioning_timers[index].is_timer_running == true) + { + m_commissioning_timers[index].current_value_sec -= COMMISSIONING_TICK_INTERVAL_SEC; + + if (m_commissioning_timers[index].current_value_sec == 0) + { + commissioning_timer_stop_reset(index); + m_commissioning_timers[index].timeout_handler(); + } + } + } +} + + +static void commissioning_timers_init(void) +{ + memset(m_commissioning_timers, 0x00, sizeof(m_commissioning_timers)); + m_commissioning_timers[TIMER_INDEX_DELAYED_ACTION].timeout_handler = \ + action_timeout_handler; + m_commissioning_timers[TIMER_INDEX_CONFIG_MODE].timeout_handler = \ + config_mode_timeout_handler; + m_commissioning_timers[TIMER_INDEX_JOINING_MODE].timeout_handler = \ + joining_mode_timeout_handler; + m_commissioning_timers[TIMER_INDEX_IDENTITY_MODE].timeout_handler = \ + identity_mode_timeout_handler; +} + +uint32_t commissioning_init(commissioning_init_params_t * p_init_param, \ + uint8_t * p_poweron_state) +{ + COMM_ENTRY(); + uint32_t err_code = NRF_SUCCESS; + + m_commissioning_evt_handler = p_init_param->commissioning_evt_handler; + m_power_off_on_failure = false; + + // Initialize Commissioning timers. + + commissioning_timers_init(); + + // Initialize GATT server. + + err_code = ble_ncfgs_init(on_ble_ncfgs_evt); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + +#if (FDS_ENABLED == 1) + err_code = persistent_settings_init(); + if (err_code != NRF_SUCCESS) + { + return err_code; + } +#endif + + // Read application settings from persistent storage. + read_node_settings(); + +#if (COMM_ENABLE_LOGS == 1) + print_node_settings(); +#endif // (COMM_ENABLE_LOGS == 1) + + if (!settings_are_valid()) // If the settings are invalid for any reason go to Config mode. + { + COMM_ERR("Invalid settings!"); + + commissioning_settings_clear(); + + memset(&m_node_settings, 0x00, sizeof(m_node_settings)); + m_node_settings.config_mode_to = 300; + + *p_poweron_state = NODE_MODE_CONFIG; + } + else + { + if (m_node_settings.poweron_mode == NODE_MODE_JOINING) + { + /* This code will get executed in two scenarios: + - if the previous mode was Config mode and now we are ready to connect to the router, or + - if the previous mode was Joining mode and the state on failure was set to No Change. + */ + if ((m_node_settings.joining_mode_failure == NCFGS_SOF_PWR_OFF) || \ + (m_node_settings.joining_mode_failure == NCFGS_SOF_CONFIG_MODE)) + { + // If the state on failure is NOT No Change, start next time in Config mode. + m_node_settings.poweron_mode = NODE_MODE_CONFIG; +#if (FDS_ENABLED == 1) + err_code = persistent_settings_update(); + APP_ERROR_CHECK(err_code); +#endif // FDS_ENABLED + } + + if (m_node_settings.joining_mode_failure == NCFGS_SOF_PWR_OFF) + { + COMM_TRC("Will power off on failure."); + m_power_off_on_failure = true; // The assert handler will power off the system. + } + + *p_poweron_state = NODE_MODE_JOINING; + } + else + { + /* The app is about to enter Config mode. Regardless of what the state on failure + setting is (No Change or Pwr Off or Cfg Mode), the poweron_mode value should remain the same. */ + + if (m_node_settings.config_mode_failure == NCFGS_SOF_PWR_OFF) + { + COMM_TRC("Will power off on failure."); + m_power_off_on_failure = true; // The assert handler will power off the system. + } + + *p_poweron_state = NODE_MODE_CONFIG; + } + } + + // Set advertising and GAP parameters. + config_mode_gap_params_set(); + config_mode_adv_params_set(); + joining_mode_gap_params_set(); + joining_mode_adv_params_set(); + + COMM_EXIT(); + return err_code; +} + +#endif // COMMISSIONING_ENABLED diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/commissioning/commissioning.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/commissioning/commissioning.h new file mode 100644 index 0000000..777f60e --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/commissioning/commissioning.h @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2015-2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup commissioning_module Commissioning Module + * @{ + * @ingroup iot_sdk_common + * @brief Commissioning module. + * + * @details Enables commissioning of the node by managing transitions between the Config, Joining, and + * Identity modes. In Config mode the node can be configured with the settings required to + * join the network in Joining mode. The Identity mode can be requested to make the node + * easily recognizable for the user. + * The settings managed by the module are stored in persistent storage. + */ + +#ifndef COMMISSIONING_H__ +#define COMMISSIONING_H__ + +#include <stdint.h> +#include <stdbool.h> +#include "ble_ncfgs.h" +#include "iot_timer.h" +#include "ipv6_medium_ble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define COMMISSIONING_TICK_INTERVAL_SEC 1 /**< Interval between periodic callbacks to the Commissioning module. */ + +#define COMMISSIONING_EVT_CONFIG_MODE_ENTER 0x01 /**< Indicates that the medium entered mode for commissioning configuration. */ +#define COMMISSIONING_EVT_JOINING_MODE_ENTER 0x02 /**< Indicates that the medium exited mode for commissioning configuration. */ +#define COMMISSIONING_EVT_IDENTITY_MODE_ENTER 0x03 /**< Indicates that identity mode was requested. */ +#define COMMISSIONING_EVT_IDENTITY_MODE_EXIT 0x04 /**< Indicates that the node should stop using any features associated with the Identity mode. */ + +#define NODE_MODE_NONE 0x00 /**< Node mode: before initialization. */ +#define NODE_MODE_JOINING 0x01 /**< Node mode: joining the network. */ +#define NODE_MODE_CONFIG 0x02 /**< Node mode: configuration. */ +#define NODE_MODE_IDENTITY 0x03 /**< Node mode: conspicuous for the user. */ + +/**@brief Joining mode timer control commands. */ +typedef enum +{ + JOINING_MODE_TIMER_START = 0x01, + JOINING_MODE_TIMER_STOP_RESET = 0x02 +} joining_mode_timer_ctrl_cmd_t; + +/**@brief Structure for storing all settings necessary for commissioning. */ +typedef struct __attribute__ ((__packed__)) __attribute__((aligned)) +{ + uint8_t poweron_mode; // Checked at startup to enter correct mode. + state_on_failure_t joining_mode_failure; // Mode to enter if Joining mode fails. + uint32_t joining_mode_to; // General timeout in Joining mode. + state_on_failure_t config_mode_failure; // Mode to enter if Config mode fails. + uint32_t config_mode_to; // General timeout in Config mode. + uint32_t id_mode_to; // Duration of Identity Mode. + ssid_store_t ssid_store; // SSID received from the router. + keys_store_t keys_store; // Keys received from the router. + id_data_store_t id_data_store; // Custom node identifier data. +} commissioning_settings_t; + +/**@brief Commissioning module event handler type. */ +typedef void (*commissioning_timeout_handler_t)(void); + +/**@brief Structure for creating timers in the Commissioning module. */ +typedef struct +{ + bool is_timer_running; + uint32_t current_value_sec; + commissioning_timeout_handler_t timeout_handler; +} commissioning_timer_t; + +/**@brief Structure of events passed by the Commissioning module to the parent layer. */ +typedef struct +{ + uint8_t commissioning_evt_id; + bool power_off_enable_requested; + commissioning_settings_t * p_commissioning_settings; +} commissioning_evt_t; + +/**@brief Function for handling BLE events. + * + * @details This function must be called from the BLE stack event dispatcher + * to handle BLE events that are relevant for the Commissioning module. + * + * @param[in] p_ble_evt BLE stack event. + */ +void commissioning_ble_evt_handler(const ble_evt_t * p_ble_evt); + +/**@brief Commissioning module event handler type. */ +typedef void (*commissioning_evt_handler_t)(commissioning_evt_t * p_commissioning_evt); + +/**@brief Structure for initialization parameters of the Commissioning module. */ +typedef struct +{ + commissioning_evt_handler_t commissioning_evt_handler; +} commissioning_init_params_t; + +/**@brief Function for initializing the Commissioning module. + * + * @details Initializes the Node Configuration Service module to create the GATT database. + * Loads previously stored node settings from the persistent storage and if + * the settings are valid, sets up the node to start in the right mode. + * + * @param[in] p_init_param Pointer to the initialization parameters. + * @param[out] p_poweron_state Pointer to the value of the mode that should be started. + * + * @retval NRF_SUCCESS If initialization was successful. Otherwise, a propagated + * error code is returned. + * + */ +uint32_t commissioning_init(commissioning_init_params_t * p_init_param, + uint8_t * p_poweron_state); + +/**@brief Function for advancing the node to a new mode. + * + * @details Stops and starts app timers appropriate for the mode requested. + * Propagates the mode change event to the parent layer. + * + * @param[in] new_mode New mode to start. + * + */ +void commissioning_node_mode_change(uint8_t new_mode); + +/**@brief Function for getting the address of GAP parameters for the active mode. + * + * @param[out] pp_node_gap_params Address of GAP parameters for the active mode. + * + */ +void commissioning_gap_params_get(ipv6_medium_ble_gap_params_t ** pp_node_gap_params); + +/**@brief Function for getting the address of advertising parameters for the active mode. + * + * @param[out] pp_node_adv_params Address of advertising parameters for the active mode. + * + */ +void commissioning_adv_params_get(ipv6_medium_ble_adv_params_t ** pp_node_adv_params); + +/**@brief Function for clearing all node settings from the persistent storage. + * + * @details Calls the appropriate persistent storage interface function to clear + * all commissioning-related settings from the persistent storage. + * + */ +void commissioning_settings_clear(void); + +/**@brief Function for controlling the joining mode timer from the parent layer(s). + * + * @details If the Joining mode timer reaches zero, the node must enter the + * state-on-failure, as set by the user. This function allows the + * application designer to control the Joining mode timer from the + * application layer. + */ +void commissioning_joining_mode_timer_ctrl( + joining_mode_timer_ctrl_cmd_t joining_mode_timer_ctrl_cmd); + +/**@brief Commissioning time tick used for measuring delays and time between events. + * + * @param[in] wall_clock_value Wall clock value from the IoT Timer module. + */ +void commissioning_time_tick(iot_timer_time_in_ms_t wall_clock_value); + +#ifdef __cplusplus +} +#endif + +#endif // COMMISSIONING_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium.h new file mode 100644 index 0000000..064737d --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium.h @@ -0,0 +1,238 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup ipv6_medium IPv6 Medium + * @{ + * @ingroup iot_sdk_common + * @brief IPv6 Medium Interface. + * + * @details Implementation-agnostic interface of the physical transport that + * facilitates IPv6 traffic. + */ + +#ifndef IPV6_MEDIUM_H__ +#define IPV6_MEDIUM_H__ + +#include <stdint.h> +#include <stdbool.h> +#include "ipv6_medium_platform.h" +#include "iot_defines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EUI_48_SIZE 6 /**< Size of a 48-bit Extended Unique Identifier in bytes. */ + +#define IPV6_MEDIUM_ID_ANY 0x00 /**< Indicates invalid physical transport type. */ +#define IPV6_MEDIUM_ID_BLE 0x01 /**< Indicates that the physical transport is BLE. */ +#define IPV6_MEDIUM_ID_802154 0x02 /**< Indicates that the physical transport is 802.15.4. */ + +#define IPV6_MEDIUM_EVT_CONN_DOWN 0x01 /**< Indicates that a connection is established. */ +#define IPV6_MEDIUM_EVT_CONN_UP 0x02 /**< Indicates that a connection is torn down. */ +#define IPV6_MEDIUM_EVT_CONNECTABLE_MODE_ENTER 0x01 /**< Indicates that the medium entered connectable mode. */ +#define IPV6_MEDIUM_EVT_CONNECTABLE_MODE_EXIT 0x02 /**< Indicates that the medium exited connectable mode. */ +#define IPV6_MEDIUM_EVT_MAC_ADDRESS_CHANGED 0x03 /**< Indicates that the device has a new MAC address. */ +#define IPV6_MEDIUM_EVT_PHY_SPECIFIC 0xFF /**< Indicates miscellaneous event from the physical layer. */ + +/**@brief IPv6 medium instance identifier type. */ +typedef uint32_t ipv6_medium_instance_id_t; + +/**@brief Type of IPv6 medium type. */ +typedef uint8_t ipv6_medium_type_t; + +/**@brief IPv6 medium instance type. */ +typedef struct +{ + ipv6_medium_instance_id_t ipv6_medium_instance_id; + ipv6_medium_type_t ipv6_medium_instance_type; +} ipv6_medium_instance_t; + +/**@brief EUI-48 value type. */ +typedef struct +{ + uint8_t identifier[EUI_48_SIZE]; /**< 48-bit identifier. */ +} eui48_t; + +/**@brief Type of IPv6 medium event parameters. */ +typedef struct +{ + ipv6_medium_instance_t ipv6_medium_instance_id; + uint8_t ipv6_medium_evt_id; + ipv6_medium_cb_params_union_t medium_specific; +} ipv6_medium_evt_t; + +/**@brief Type of IPv6 medium error parameters. */ +typedef struct +{ + ipv6_medium_instance_t ipv6_medium_instance_id; + uint32_t error_label; + ipv6_medium_err_params_union_t medium_specific; +} ipv6_medium_error_t; + +/**@brief IPv6 medium event handler type. */ +typedef void (*ipv6_medium_evt_handler_t)(ipv6_medium_evt_t * p_ipv6_medium_evt); + +/**@brief IPv6 medium error handler type. */ +typedef void (*ipv6_medium_error_handler_t)(ipv6_medium_error_t * p_ipv6_medium_error); + +#ifdef COMMISSIONING_ENABLED +/**@brief Commissioning mode control commands. */ +typedef enum +{ + CMD_IDENTITY_MODE_EXIT = 0x00, + CMD_IDENTITY_MODE_ENTER = 0x01 +} mode_control_cmd_t; + +/**@brief Commissioning: Identity mode control callback function type. */ +typedef void (*commissioning_id_mode_cb_t)(mode_control_cmd_t control_command); + +/**@brief Commissioning: Power off on failure control callback function type. */ +typedef void (*commissioning_poweroff_cb_t)(bool do_poweroff_on_failure); +#endif // COMMISSIONING_ENABLED + +/**@brief Structure for initialization parameters of the IPv6 medium. */ +typedef struct +{ + ipv6_medium_evt_handler_t ipv6_medium_evt_handler; + ipv6_medium_error_handler_t ipv6_medium_error_handler; +#ifdef COMMISSIONING_ENABLED + commissioning_id_mode_cb_t commissioning_id_mode_cb; + commissioning_poweroff_cb_t commissioning_power_off_cb; +#endif // COMMISSIONING_ENABLED +} ipv6_medium_init_params_t; + +/**@brief Function for initializing the IPv6 medium. + * + * @details Initializes the IPv6 medium module. + * Performs all setup necessary that is specific to the implementation. + * + * @param[in] p_init_param Pointer to the initialization parameters. + * @param[in] desired_medium_type Value of the desired medium type. + * @param[out] p_new_medium_instance Pointer to the new medium instance initialized. + * + * @retval NRF_SUCCESS If initialization was successful. Otherwise, a propagated + * error code is returned. + * + */ +uint32_t ipv6_medium_init(ipv6_medium_init_params_t * p_init_param, + ipv6_medium_type_t desired_medium_type, + ipv6_medium_instance_t * p_new_medium_instance); + +/**@brief Function for entering connectible mode. + * + * @details Requests the IPv6 medium to enter connectible mode. + * + * @param[in] ipv6_medium_instance_id Specifies the IPv6 medium instance. + * + * @retval NRF_SUCCESS If the procedure was successful. Otherwise, a propagated + * error code is returned. + * + */ +uint32_t ipv6_medium_connectable_mode_enter(ipv6_medium_instance_id_t ipv6_medium_instance_id); + +/**@brief Function for exiting connectible mode. + * + * @details Requests the IPv6 medium to exit connectible mode. + * + * @param[in] ipv6_medium_instance_id Specifies the IPv6 medium instance. + * + * @retval NRF_SUCCESS If the procedure was successful. Otherwise, a propagated + * error code is returned. + * + */ +uint32_t ipv6_medium_connectable_mode_exit(ipv6_medium_instance_id_t ipv6_medium_instance_id); + +/**@brief Function for getting the 48-bit Extended Unique Identifier. + * + * @param[in] ipv6_medium_instance_id Specifies the IPv6 medium instance. + * @param[out] p_ipv6_medium_eui48 Pointer to the EUI-48 value. + * + * @retval NRF_SUCCESS If the procedure was successful. Otherwise, a propagated + * error code is returned. + * + */ +uint32_t ipv6_medium_eui48_get(ipv6_medium_instance_id_t ipv6_medium_instance_id, + eui48_t * p_ipv6_medium_eui48); + +/**@brief Function for setting the 48-bit Extended Unique Identifier. + * + * @param[in] ipv6_medium_instance_id Specifies the IPv6 medium instance. + * @param[in] p_ipv6_medium_eui48 Pointer to the EUI-48 value. + * + * @retval NRF_SUCCESS If the procedure was successful. Otherwise, a propagated + * error code is returned. + * + */ +uint32_t ipv6_medium_eui48_set(ipv6_medium_instance_id_t ipv6_medium_instance_id, + eui48_t * p_ipv6_medium_eui48); + +/**@brief Function for getting the 64-bit Extended Unique Identifier. + * + * @param[in] ipv6_medium_instance_id Specifies the IPv6 medium instance. + * @param[out] p_ipv6_medium_eui64 Pointer to the EUI-64 value. + * + * @retval NRF_SUCCESS If the procedure was successful. Otherwise, a propagated + * error code is returned. + * + */ +uint32_t ipv6_medium_eui64_get(ipv6_medium_instance_id_t ipv6_medium_instance_id, + eui64_t * p_ipv6_medium_eui64); + +/**@brief Function for setting the 64-bit Extended Unique Identifier. + * + * @param[in] ipv6_medium_instance_id Specifies the IPv6 medium instance. + * @param[in] p_ipv6_medium_eui64 Pointer to the EUI-64 value. + * + * @retval NRF_SUCCESS If the procedure was successful. Otherwise, a propagated + * error code is returned. + * + */ +uint32_t ipv6_medium_eui64_set(ipv6_medium_instance_id_t ipv6_medium_instance_id, + eui64_t * p_ipv6_medium_eui64); + +#ifdef __cplusplus +} +#endif + +#endif // IPV6_MEDIUM_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium_ble.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium_ble.c new file mode 100644 index 0000000..0d0a415 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium_ble.c @@ -0,0 +1,689 @@ +/** + * Copyright (c) 2015-2018 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdint.h> +#include "boards.h" +#include "ipv6_medium.h" +#include "ipv6_medium_ble.h" +#include "ble_advdata.h" +#include "ble_hci.h" +#include "ble_srv_common.h" +#include "ble_ipsp.h" +#include "sdk_config.h" +#include "nrf_sdm.h" +#include "nrf_sdh.h" +#include "nrf_sdh_ble.h" +#ifdef COMMISSIONING_ENABLED +#include "commissioning.h" +#endif // COMMISSIONING_ENABLED + +#define PUBLIC_BLE_GAP_ADDR_CREATE_FROM_EUI64(ble_gap_addr, eui64, ble_gap_addr_type) \ + ble_gap_addr[0] = eui64[7]; \ + ble_gap_addr[1] = eui64[6]; \ + ble_gap_addr[2] = eui64[5]; \ + ble_gap_addr[3] = eui64[2]; \ + ble_gap_addr[4] = eui64[1]; \ + ble_gap_addr[5] = 0x00; \ + ble_gap_addr_type = BLE_GAP_ADDR_TYPE_PUBLIC; + +#define IOT_TIMER_DISABLE_API_PARAM_CHECK 0 + +#if (IOT_TIMER_DISABLE_API_PARAM_CHECK == 0) + +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL); \ + } + +#else // IOT_TIMER_DISABLE_API_PARAM_CHECK + +#define NULL_PARAM_CHECK(PARAM) + +#endif //IOT_TIMER_DISABLE_API_PARAM_CHECK + +#define BLE_IPV6_MEDIUM_BLE_OBSERVER_PRIO 1 /**< BLE observer priority. */ +#define BLE_IPSP_TAG 35 /**< Identifies the L2CAP configuration used with SoftDevice. */ + + +static ipv6_medium_instance_id_t m_module_instance_id = 0x01; /**< Module instance identifier. As of today, only a single instance is supported. */ +static ipv6_medium_evt_handler_t m_ipv6_medium_evt_handler; /**< Pointer to the event handler procedure of the parent layer. */ +static ipv6_medium_error_handler_t m_ipv6_medium_error_handler; /**< Pointer to the error handler procedure of the parent layer. */ +static ble_gap_addr_t m_local_ble_addr; /**< Local BT device address. */ +static ipv6_medium_ble_gap_params_t * m_p_node_gap_params; /**< Pointer to advertising parameters to be used. */ +static ipv6_medium_ble_adv_params_t * m_p_node_adv_params; /**< Pointer to GAP parameters to be used. */ + +#ifndef COMMISSIONING_ENABLED +static ipv6_medium_ble_gap_params_t m_gap_params; /**< Advertising parameters w/o commissioning. */ +static ipv6_medium_ble_adv_params_t m_adv_params; /**< GAP parameters w/o commissioning. */ +static ble_uuid_t m_adv_uuids[] = + { + {BLE_UUID_IPSP_SERVICE, BLE_UUID_TYPE_BLE} + }; /**< List of available service UUIDs in advertisement data. */ +#else // COMMISSIONING_ENABLED +static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the active connection. */ +static bool m_connectable_mode_active = false; /**< Indicates if the node is in connectable mode. */ +static commissioning_id_mode_cb_t m_commissioning_id_mode_cb; +static commissioning_poweroff_cb_t m_commissioning_power_off_cb; +static bool m_adv_params_applied = false; /**< Indicates if advertising (and GAP) parameters have been applied. */ +#endif // COMMISSIONING_ENABLED + +static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; /**< Advertising handle used to identify an advertising set. */ +static uint8_t m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX]; /**< Buffer for storing an encoded advertising set. */ + +/**@brief Struct that contains pointers to the encoded advertising data. */ +static ble_gap_adv_data_t m_adv_data = +{ + .adv_data = + { + .p_data = m_enc_advdata, + .len = BLE_GAP_ADV_SET_DATA_SIZE_MAX + }, + .scan_rsp_data = + { + .p_data = NULL, + .len = 0 + + } +}; + +#if IPV6_MEDIUM_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME ipv6_medium + +#define NRF_LOG_LEVEL IPV6_MEDIUM_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR IPV6_MEDIUM_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR IPV6_MEDIUM_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define IPV6M_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define IPV6M_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define IPV6M_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define IPV6M_ENTRY() IPV6M_TRC(">> %s", __func__) +#define IPV6M_EXIT() IPV6M_TRC("<< %s", __func__) + +#else // IPV6_MEDIUM_CONFIG_LOG_ENABLED + +#define IPV6M_TRC(...) /**< Disables traces. */ +#define IPV6M_DUMP(...) /**< Disables dumping of octet streams. */ +#define IPV6M_ERR(...) /**< Disables error logs. */ + +#define IPV6M_ENTRY(...) +#define IPV6M_EXIT(...) + +#endif // IPV6_MEDIUM_CONFIG_LOG_ENABLED + + +#ifndef COMMISSIONING_ENABLED + +/**@brief Function for setting advertisement parameters. + * + * @details These parameters are applied if the Commissioning module is + * not used or in Joining mode. + */ +static void adv_params_set(void) +{ + IPV6M_ENTRY(); + memset(&m_adv_params, 0x00, sizeof(m_adv_params)); + + m_adv_params.advdata.name_type = BLE_ADVDATA_FULL_NAME; + m_adv_params.advdata.flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED; + m_adv_params.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]); + m_adv_params.advdata.uuids_complete.p_uuids = m_adv_uuids; + + m_adv_params.advparams.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; + m_adv_params.advparams.p_peer_addr = NULL; // Undirected advertisement. + m_adv_params.advparams.filter_policy = BLE_GAP_ADV_FP_ANY; + m_adv_params.advparams.interval = APP_ADV_ADV_INTERVAL; + m_adv_params.advparams.duration = APP_ADV_DURATION; + + IPV6M_EXIT(); +} + +/**@brief Function for setting GAP parameters. + * + * @details These parameters are applied if the Commissioning module is + * not used or in Joining mode. + */ +static void gap_params_set(void) +{ + memset(&m_gap_params, 0x00, sizeof(m_gap_params)); + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&m_gap_params.sec_mode); + + m_gap_params.appearance = BLE_APPEARANCE_UNKNOWN; + + m_gap_params.p_dev_name = (const uint8_t *)DEVICE_NAME; + m_gap_params.dev_name_len = strlen(DEVICE_NAME); + + m_gap_params.gap_conn_params.min_conn_interval = (uint16_t)MIN_CONN_INTERVAL; + m_gap_params.gap_conn_params.max_conn_interval = (uint16_t)MAX_CONN_INTERVAL; + m_gap_params.gap_conn_params.slave_latency = SLAVE_LATENCY; + m_gap_params.gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; +} + +#endif // COMMISSIONING_ENABLED + + +/**@brief Function for applying the advertisement parameters. + * + * @details Encodes the required advertising data and passes it to the stack. + */ +static void adv_params_apply(void) +{ + uint32_t err_code; + + + err_code = ble_advdata_encode(&m_p_node_adv_params->advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len); + APP_ERROR_CHECK(err_code); +#ifndef COMMISSIONING_ENABLED + err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &m_adv_params.advparams); + APP_ERROR_CHECK(err_code); +#else + err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &m_p_node_adv_params->advparams); + APP_ERROR_CHECK(err_code); +#endif +} + + +/**@brief Function for applying the GAP configuration. + * + * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the + * device including the device name, appearance, and the preferred connection parameters. + */ +static void gap_params_apply(void) +{ + uint32_t err_code; + + err_code = sd_ble_gap_device_name_set(&m_p_node_gap_params->sec_mode, \ + m_p_node_gap_params->p_dev_name, \ + m_p_node_gap_params->dev_name_len); + APP_ERROR_CHECK(err_code); + + err_code = sd_ble_gap_appearance_set(m_p_node_gap_params->appearance); + APP_ERROR_CHECK(err_code); + + err_code = sd_ble_gap_ppcp_set(&m_p_node_gap_params->gap_conn_params); + APP_ERROR_CHECK(err_code); +} + + +/**@brief Function for handling the application's BLE Stack events and + * passing them on to the applications as generic transport medium events. + * + * @param[in] p_ble_evt Bluetooth stack event. + */ +static void on_ble_evt(ble_evt_t const * p_ble_evt) +{ + ipv6_medium_evt_t ipv6_medium_evt; + + memset(&ipv6_medium_evt, 0x00, sizeof(ipv6_medium_evt)); + ipv6_medium_evt.ipv6_medium_instance_id.ipv6_medium_instance_id = m_module_instance_id; + ipv6_medium_evt.ipv6_medium_instance_id.ipv6_medium_instance_type = IPV6_MEDIUM_ID_BLE; + ipv6_medium_evt.medium_specific.ble.p_ble_evt = (ble_evt_t*)p_ble_evt; + + ipv6_medium_error_t ipv6_medium_error; + memset(&ipv6_medium_error, 0x00, sizeof(ipv6_medium_error)); + ipv6_medium_error.ipv6_medium_instance_id.ipv6_medium_instance_id = m_module_instance_id; + ipv6_medium_error.ipv6_medium_instance_id.ipv6_medium_instance_type = IPV6_MEDIUM_ID_BLE; + + bool do_notify_event = false; + bool do_notify_error = false; + + switch (p_ble_evt->header.evt_id) + { + case BLE_GAP_EVT_CONNECTED: + { +#ifdef COMMISSIONING_ENABLED + m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; +#endif // COMMISSIONING_ENABLED + ipv6_medium_evt.ipv6_medium_evt_id = IPV6_MEDIUM_EVT_CONN_UP; + do_notify_event = true; + + break; + } + case BLE_GAP_EVT_DISCONNECTED: + { +#ifdef COMMISSIONING_ENABLED + m_conn_handle = BLE_CONN_HANDLE_INVALID; +#endif // COMMISSIONING_ENABLED + ipv6_medium_evt.ipv6_medium_evt_id = IPV6_MEDIUM_EVT_CONN_DOWN; + do_notify_event = true; + + break; + } + case BLE_GAP_EVT_ADV_SET_TERMINATED: + { + if (p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_TIMEOUT) + { + ipv6_medium_evt.ipv6_medium_evt_id = IPV6_MEDIUM_EVT_CONNECTABLE_MODE_EXIT; + do_notify_event = true; + } + else + { + // This is not necessarily an error, only added here to show error handler usage. + ipv6_medium_error.medium_specific.ble.dummy_value = 0x13; + do_notify_error = true; + } + break; + } + default: + { + ipv6_medium_evt.ipv6_medium_evt_id = IPV6_MEDIUM_EVT_PHY_SPECIFIC; + do_notify_event = true; + + break; + } + } + + ble_ipsp_evt_handler(p_ble_evt); + + if (do_notify_event == true) + { + m_ipv6_medium_evt_handler(&ipv6_medium_evt); + } + if (do_notify_error == true) + { + m_ipv6_medium_error_handler(&ipv6_medium_error); + } +} + +/* + * @brief Function for handling BLE events. + * + * @param[in] p_ble_evt Event received from the BLE stack. + * @param[in] p_context Context. + */ +static void ble_evt_handler(const ble_evt_t * p_ble_evt, void * p_context) +{ + UNUSED_PARAMETER(p_context); +#ifdef COMMISSIONING_ENABLED + commissioning_ble_evt_handler(p_ble_evt); + ble_ncfgs_ble_evt_handler(p_ble_evt); +#endif // COMMISSIONING_ENABLED + + on_ble_evt(p_ble_evt); +} + + + + +/**@brief Function for initializing the BLE stack. + * + * @details Initializes the SoftDevice and the BLE event interrupt. + */ +static uint32_t ble_stack_init(void) +{ + ret_code_t err_code; + uint32_t ram_start = 0; + ble_cfg_t ble_cfg; + + err_code = nrf_sdh_enable_request(); + + if (err_code == NRF_SUCCESS) + { + // Fetch the start address of the application RAM. + err_code = nrf_sdh_ble_app_ram_start_get(&ram_start); + } + + if (err_code == NRF_SUCCESS) + { + // Configure the maximum number of connections. + memset(&ble_cfg, 0, sizeof(ble_cfg)); + ble_cfg.gap_cfg.role_count_cfg.periph_role_count = BLE_IPSP_MAX_CHANNELS; + ble_cfg.gap_cfg.role_count_cfg.central_role_count = 0; + ble_cfg.gap_cfg.role_count_cfg.central_sec_count = 0; + err_code = sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &ble_cfg, ram_start); + } + + if (err_code == NRF_SUCCESS) + { + memset(&ble_cfg, 0, sizeof(ble_cfg)); + + // Configure total number of connections. + ble_cfg.conn_cfg.conn_cfg_tag = BLE_IPSP_TAG; + ble_cfg.conn_cfg.params.gap_conn_cfg.conn_count = BLE_IPSP_MAX_CHANNELS; + ble_cfg.conn_cfg.params.gap_conn_cfg.event_length = BLE_GAP_EVENT_LENGTH_DEFAULT; + err_code = sd_ble_cfg_set(BLE_CONN_CFG_GAP, &ble_cfg, ram_start); + + } + + if (err_code == NRF_SUCCESS) + { + memset(&ble_cfg, 0, sizeof(ble_cfg)); + + // Configure the number of custom UUIDS. +#ifdef COMMISSIONING_ENABLED + ble_cfg.common_cfg.vs_uuid_cfg.vs_uuid_count = 1; +#else + ble_cfg.common_cfg.vs_uuid_cfg.vs_uuid_count = 0; +#endif // COMMISSIONING_ENABLED + + err_code = sd_ble_cfg_set(BLE_COMMON_CFG_VS_UUID, &ble_cfg, ram_start); + } + + if (err_code == NRF_SUCCESS) + { + memset(&ble_cfg, 0, sizeof(ble_cfg)); + + // Set L2CAP channel configuration + ble_cfg.conn_cfg.conn_cfg_tag = BLE_IPSP_TAG; + ble_cfg.conn_cfg.params.l2cap_conn_cfg.rx_mps = BLE_IPSP_RX_MPS; + ble_cfg.conn_cfg.params.l2cap_conn_cfg.rx_queue_size = BLE_IPSP_RX_BUFFER_COUNT; + ble_cfg.conn_cfg.params.l2cap_conn_cfg.tx_mps = BLE_IPSP_TX_MPS; + ble_cfg.conn_cfg.params.l2cap_conn_cfg.tx_queue_size = 1; + ble_cfg.conn_cfg.params.l2cap_conn_cfg.ch_count = 1; // One IPSP channel per link. + err_code = sd_ble_cfg_set(BLE_CONN_CFG_L2CAP, &ble_cfg, ram_start); + } + + if (err_code == NRF_SUCCESS) + { + memset(&ble_cfg, 0, sizeof(ble_cfg)); + + // Set the ATT table size. +#ifdef COMMISSIONING_ENABLED + ble_cfg.gatts_cfg.attr_tab_size.attr_tab_size = 1024; +#else + ble_cfg.gatts_cfg.attr_tab_size.attr_tab_size = 256; +#endif // COMMISSIONING_ENABLED + err_code = sd_ble_cfg_set(BLE_GATTS_CFG_ATTR_TAB_SIZE, &ble_cfg, ram_start); + } + + if (err_code == NRF_SUCCESS) + { + err_code = nrf_sdh_ble_enable(&ram_start); + } + + NRF_SDH_BLE_OBSERVER(m_ble_evt_observer, BLE_IPV6_MEDIUM_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); + + return err_code; +} + + +uint32_t ipv6_medium_connectable_mode_enter(ipv6_medium_instance_id_t ipv6_medium_instance_id) +{ + IPV6M_ENTRY(); + + if (ipv6_medium_instance_id != m_module_instance_id) + { + return NRF_ERROR_INVALID_PARAM; + } + +#ifdef COMMISSIONING_ENABLED + if (m_adv_params_applied == false) + { + // Apply advertising (and GAP) parameters, if not applied when node mode changed. + commissioning_gap_params_get(&m_p_node_gap_params); + commissioning_adv_params_get(&m_p_node_adv_params); + gap_params_apply(); + adv_params_apply(); + } + m_adv_params_applied = false; +#endif // COMMISSIONING_ENABLED + + adv_params_apply(); + + uint32_t err_code = sd_ble_gap_adv_start(m_adv_handle, BLE_IPSP_TAG); +#ifdef COMMISSIONING_ENABLED + if (err_code == NRF_SUCCESS) + { + m_connectable_mode_active = true; + } +#endif // COMMISSIONING_ENABLED + IPV6M_EXIT(); + return err_code; +} + + +uint32_t ipv6_medium_connectable_mode_exit(ipv6_medium_instance_id_t ipv6_medium_instance_id) +{ + if (ipv6_medium_instance_id != m_module_instance_id) + { + return NRF_ERROR_INVALID_PARAM; + } + + uint32_t err_code = sd_ble_gap_adv_stop(m_adv_handle); +#ifdef COMMISSIONING_ENABLED + if (err_code == NRF_SUCCESS) + { + m_connectable_mode_active = false; + } +#endif // COMMISSIONING_ENABLED + return err_code; +} + + +uint32_t ipv6_medium_eui48_get(ipv6_medium_instance_id_t ipv6_medium_instance_id, \ + eui48_t * p_ipv6_medium_eui48) +{ + if (ipv6_medium_instance_id != m_module_instance_id) + { + return NRF_ERROR_INVALID_PARAM; + } + ble_gap_addr_t local_ble_addr; + uint32_t err_code = sd_ble_gap_addr_get(&local_ble_addr); + + memcpy(p_ipv6_medium_eui48->identifier, local_ble_addr.addr, EUI_48_SIZE); + + return err_code; +} + + +uint32_t ipv6_medium_eui48_set(ipv6_medium_instance_id_t ipv6_medium_instance_id, \ + eui48_t * p_ipv6_medium_eui48) +{ + if (ipv6_medium_instance_id != m_module_instance_id) + { + return NRF_ERROR_INVALID_PARAM; + } + if (p_ipv6_medium_eui48->identifier[5] != 0x00) + { + return NRF_ERROR_INVALID_PARAM; + } + + m_local_ble_addr.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC; + memcpy(m_local_ble_addr.addr, p_ipv6_medium_eui48->identifier, EUI_48_SIZE); + + return sd_ble_gap_addr_set(&m_local_ble_addr); +} + + +uint32_t ipv6_medium_eui64_get(ipv6_medium_instance_id_t ipv6_medium_instance_id, \ + eui64_t * p_ipv6_medium_eui64) +{ + if (ipv6_medium_instance_id != m_module_instance_id) + { + return NRF_ERROR_INVALID_PARAM; + } + ble_gap_addr_t local_ble_addr; + + uint32_t err_code = sd_ble_gap_addr_get(&local_ble_addr); + APP_ERROR_CHECK(err_code); + + IPV6_EUI64_CREATE_FROM_EUI48(p_ipv6_medium_eui64->identifier, + local_ble_addr.addr, + local_ble_addr.addr_type); + return NRF_SUCCESS; +} + + +uint32_t ipv6_medium_eui64_set(ipv6_medium_instance_id_t ipv6_medium_instance_id, \ + eui64_t * p_ipv6_medium_eui64) +{ + if (ipv6_medium_instance_id != m_module_instance_id) + { + return NRF_ERROR_INVALID_PARAM; + } + if ((p_ipv6_medium_eui64->identifier[0] != 0x02) || + (p_ipv6_medium_eui64->identifier[3] != 0xFF) || + (p_ipv6_medium_eui64->identifier[4] != 0xFE)) + { + return NRF_ERROR_INVALID_PARAM; + } + + ble_gap_addr_t local_ble_addr; + + PUBLIC_BLE_GAP_ADDR_CREATE_FROM_EUI64(local_ble_addr.addr, \ + p_ipv6_medium_eui64->identifier, \ + local_ble_addr.addr_type); + + return sd_ble_gap_addr_set(&local_ble_addr); +} + + +#ifdef COMMISSIONING_ENABLED + +void commissioning_evt_handler(commissioning_evt_t * p_commissioning_evt) +{ + IPV6M_ENTRY(); + + switch (p_commissioning_evt->commissioning_evt_id) + { + case COMMISSIONING_EVT_CONFIG_MODE_ENTER: + // Fall-through. + case COMMISSIONING_EVT_JOINING_MODE_ENTER: + { + m_commissioning_power_off_cb(p_commissioning_evt->power_off_enable_requested); + + if (m_conn_handle != BLE_CONN_HANDLE_INVALID) + { + // Making sure that advertising (and GAP) parameters are + // applied when entering connectable mode the next time. + m_adv_params_applied = false; + UNUSED_VARIABLE(sd_ble_gap_disconnect(m_conn_handle, \ + BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION)); + } + else + { + bool do_return_to_connectable_mode = m_connectable_mode_active; + UNUSED_VARIABLE(ipv6_medium_connectable_mode_exit(m_module_instance_id)); + + commissioning_gap_params_get(&m_p_node_gap_params); + commissioning_adv_params_get(&m_p_node_adv_params); + gap_params_apply(); + adv_params_apply(); + // Advertising and GAP parameters applied, making sure that + // it is not repeated when entering connectable mode the next time. + m_adv_params_applied = true; + + if (do_return_to_connectable_mode == true) + { + // Restart connectable mode, if the node was in connectable mode applying + // the new parameters. + UNUSED_VARIABLE(ipv6_medium_connectable_mode_enter(m_module_instance_id)); + } + } + + break; + } + case COMMISSIONING_EVT_IDENTITY_MODE_ENTER: + { + m_commissioning_id_mode_cb(CMD_IDENTITY_MODE_ENTER); + + break; + } + case COMMISSIONING_EVT_IDENTITY_MODE_EXIT: + { + m_commissioning_id_mode_cb(CMD_IDENTITY_MODE_EXIT); + + break; + } + default: + { + // No implementation needed. + break; + } + } + + IPV6M_EXIT(); +} + +#endif // COMMISSIONING_ENABLED + + +uint32_t ipv6_medium_init(ipv6_medium_init_params_t * p_init_param, \ + ipv6_medium_type_t desired_medium_type, \ + ipv6_medium_instance_t * p_new_medium_instance) +{ + IPV6M_ENTRY(); + uint32_t err_code = NRF_SUCCESS; + NULL_PARAM_CHECK(p_init_param->ipv6_medium_evt_handler); + if (desired_medium_type != IPV6_MEDIUM_ID_BLE) + { + return NRF_ERROR_INVALID_PARAM; + } + + m_ipv6_medium_evt_handler = p_init_param->ipv6_medium_evt_handler; + m_ipv6_medium_error_handler = p_init_param->ipv6_medium_error_handler; + + p_new_medium_instance->ipv6_medium_instance_type = IPV6_MEDIUM_ID_BLE; + p_new_medium_instance->ipv6_medium_instance_id = m_module_instance_id; + + err_code = ble_stack_init(); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + +#ifndef COMMISSIONING_ENABLED + gap_params_set(); + adv_params_set(); + m_p_node_gap_params = &m_gap_params; + m_p_node_adv_params = &m_adv_params; + gap_params_apply(); +#else // COMMISSIONING_ENABLED + m_commissioning_id_mode_cb = p_init_param->commissioning_id_mode_cb; + m_commissioning_power_off_cb = p_init_param->commissioning_power_off_cb; + + commissioning_init_params_t init_param; + memset(&init_param, 0x00, sizeof(init_param)); + init_param.commissioning_evt_handler = commissioning_evt_handler; + uint8_t new_mode; + err_code = commissioning_init(&init_param, \ + &new_mode); + + commissioning_node_mode_change(new_mode); +#endif // COMMISSIONING_ENABLED + + IPV6M_EXIT(); + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium_ble.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium_ble.h new file mode 100644 index 0000000..492afe4 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium_ble.h @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup ipv6_medium_ble BLE IPv6 Medium Implementation + * @{ + * @ingroup iot_sdk_common + * @brief Bluetooth Low Energy implementation of the IPv6 medium interface. + * + * @details Type definitions for the BLE implementation of the IPv6 medium interface. + * This header also includes the header with BLE-specific configuration. + */ + +#ifndef IPV6_MEDIUM_BLE_H__ +#define IPV6_MEDIUM_BLE_H__ + +#include <stdint.h> +#include "ble.h" +#include "ble_advdata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Structure for storing all GAP parameters. */ +typedef struct +{ + uint16_t appearance; + uint8_t const * p_dev_name; + uint16_t dev_name_len; + ble_gap_conn_sec_mode_t sec_mode; + ble_gap_conn_params_t gap_conn_params; +} ipv6_medium_ble_gap_params_t; + +/**@brief Structure for storing all advertisement parameters. */ +typedef struct +{ + ble_advdata_t advdata; + ble_advdata_manuf_data_t adv_man_specific_data; + ble_advdata_t srdata; + ble_advdata_manuf_data_t sr_man_specific_data; + ble_gap_adv_params_t advparams; +} ipv6_medium_ble_adv_params_t; + +/**@brief Structure of BLE-specific parameters of events passed to the parent layer by the IPv6 medium. */ +typedef struct +{ + ble_evt_t * p_ble_evt; +} ipv6_medium_ble_cb_params_t; + +/**@brief Structure of BLE-specific parameters of errors passed to the parent layer by the IPv6 medium. */ +typedef struct +{ + uint8_t dummy_value; // Parameters to be added. +} ipv6_medium_ble_error_params_t; + +#ifdef __cplusplus +} +#endif + +#endif // IPV6_MEDIUM_BLE_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium_platform_dummy.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium_platform_dummy.h new file mode 100644 index 0000000..341d326 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/medium/ipv6_medium_platform_dummy.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef IPV6_MEDIUM_PLATFORM_DUMMY_H__ +#define IPV6_MEDIUM_PLATFORM_DUMMY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int ipv6_medium_cb_params_union_t; +typedef int ipv6_medium_err_params_union_t; + +#ifdef __cplusplus +} +#endif + +#endif // IPV6_MEDIUM_PLATFORM_DUMMY_H__ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt.c new file mode 100644 index 0000000..ecb6fcd --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt.c @@ -0,0 +1,821 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file mqtt.c + * + * @brief MQTT Client API Implementation. + */ + + +#include "mqtt.h" +#include "mem_manager.h" +#include "mqtt_transport.h" +#include "mqtt_internal.h" +#include "iot_timer.h" + +#if MQTT_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME mqtt + +#define NRF_LOG_LEVEL MQTT_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR MQTT_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR MQTT_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define MQTT_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define MQTT_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define MQTT_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define MQTT_ENTRY() MQTT_TRC(">> %s", __func__) +#define MQTT_EXIT() MQTT_TRC("<< %s", __func__) + +#else // MQTT_CONFIG_LOG_ENABLED + +#define MQTT_TRC(...) /**< Disables traces. */ +#define MQTT_DUMP(...) /**< Disables dumping of octet streams. */ +#define MQTT_ERR(...) /**< Disables error logs. */ + +#define MQTT_ENTRY(...) +#define MQTT_EXIT(...) + +#endif // MQTT_CONFIG_LOG_ENABLED + +/**< Never changing ping request, needed for Keep Alive. */ +static const uint8_t m_ping_packet[MQTT_PKT_HEADER_SIZE] = \ + {MQTT_PKT_TYPE_PINGREQ, \ + 0x00}; + +/**< Never changing disconnect request. */ +static const uint8_t m_disc_packet[MQTT_PKT_HEADER_SIZE] = \ + {MQTT_PKT_TYPE_DISCONNECT, \ + 0x00}; + +static mqtt_client_t * m_mqtt_client[MQTT_MAX_CLIENTS]; /**< MQTT Client table. */ +SDK_MUTEX_DEFINE(m_mqtt_mutex) /**< Mutex variable for the module, currently unused. */ + + +static uint32_t get_client_index(mqtt_client_t * const p_client) +{ + for (uint32_t index = 0; index < MQTT_MAX_CLIENTS; index++) + { + if (m_mqtt_client[index] == p_client) + { + return index; + } + } + + return MQTT_MAX_CLIENTS; +} + + +void client_free(mqtt_client_t * const p_client) +{ + MQTT_STATE_INIT(p_client); + + // Free memory used for TX packets and reset the pointer. + nrf_free(p_client->p_packet); + p_client->p_packet = NULL; + + // Free TLS instance and reset the instance. + UNUSED_VARIABLE(nrf_tls_free(&p_client->tls_instance)); + NRF_TLS_INTSANCE_INIT(&p_client->tls_instance); +} + + +void client_init(mqtt_client_t * const p_client) +{ + memset(p_client, 0, sizeof(*p_client)); + + MQTT_STATE_INIT(p_client); + + p_client->protocol_version = MQTT_VERSION_3_1_0; + p_client->clean_session = 1; + + NRF_TLS_INTSANCE_INIT(&p_client->tls_instance); +} + + +/**@brief Notifies event to the application. + * + * @param[in] p_client Identifies the client for which the procedure is requested. + * @param[in] p_evt Reason for disconnection. + */ +void event_notify(mqtt_client_t * const p_client, const mqtt_evt_t * p_evt, uint32_t flags) +{ + const mqtt_evt_cb_t evt_cb = p_client->evt_cb; + + if (evt_cb != NULL) + { + MQTT_MUTEX_UNLOCK(); + + evt_cb(p_client, p_evt); + + MQTT_MUTEX_LOCK(); + + if (IS_SET(flags,MQTT_EVT_FLAG_INSTANCE_RESET)) + { + client_init(p_client); + } + } +} + + +/**@brief Notifies disconnection event to the application. + * + * @param[in] p_client Identifies the client for which the procedure is requested. + * @param[in] result Reason for disconnection. + */ +void disconnect_event_notify(mqtt_client_t * p_client, uint32_t result) +{ + mqtt_evt_t evt; + const uint32_t client_index = get_client_index(p_client); + + // Remove the client from internal table. + if (client_index != MQTT_MAX_CLIENTS) + { + m_mqtt_client[client_index] = NULL; + } + + // Determine appropriate event to generate. + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_CONNECTED) || + MQTT_VERIFY_STATE(p_client, MQTT_STATE_DISCONNECTING)) + { + evt.id = MQTT_EVT_DISCONNECT; + evt.result = result; + } + else + { + evt.id = MQTT_EVT_CONNACK; + evt.result = MQTT_CONNECTION_FAILED; + } + + // Free the instance. + client_free(p_client); + + // Notify application. + event_notify(p_client, &evt, MQTT_EVT_FLAG_INSTANCE_RESET); +} + + +uint32_t mqtt_init(void) +{ + SDK_MUTEX_INIT(m_mqtt_mutex); + + MQTT_MUTEX_LOCK(); + + memset(m_mqtt_client, 0, sizeof(m_mqtt_client)); + + MQTT_MUTEX_UNLOCK(); + + return nrf_tls_init(); +} + + +void mqtt_client_init(mqtt_client_t * const p_client) +{ + NULL_PARAM_CHECK_VOID(p_client); + + MQTT_MUTEX_LOCK(); + + client_init(p_client); + + MQTT_MUTEX_UNLOCK(); +} + + +uint32_t mqtt_connect(mqtt_client_t * const p_client) +{ + // Look for a free instance if available. + uint32_t err_code = NRF_SUCCESS; + uint32_t client_index = 0; + + NULL_PARAM_CHECK(p_client); + NULL_PARAM_CHECK(p_client->client_id.p_utf_str); + + MQTT_MUTEX_LOCK(); + + for (client_index = 0; client_index < MQTT_MAX_CLIENTS; client_index++) + { + if (m_mqtt_client[client_index] == NULL) + { + // Found a free instance. + m_mqtt_client[client_index] = p_client; + + // Allocate buffer packets in TX path. + p_client->p_packet = nrf_malloc(MQTT_MAX_PACKET_LENGTH); + break; + } + } + + if ((client_index == MQTT_MAX_CLIENTS) || (p_client->p_packet == NULL)) + { + err_code = (NRF_ERROR_NO_MEM | IOT_MQTT_ERR_BASE); + } + else + { + err_code = tcp_request_connection(p_client); + + if (err_code != NRF_SUCCESS) + { + // Free the instance. + m_mqtt_client[client_index] = NULL; + nrf_free(p_client->p_packet); + err_code = MQTT_ERR_TCP_PROC_FAILED; + } + } + + UNUSED_VARIABLE(p_client); + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t mqtt_publish(mqtt_client_t * const p_client, + mqtt_publish_param_t const * const p_param) +{ + uint32_t err_code = MQTT_ERR_NOT_CONNECTED; + uint32_t offset = 0; + uint32_t mqtt_packetlen = 0; + uint8_t * p_payload; + + NULL_PARAM_CHECK(p_client); + NULL_PARAM_CHECK(p_param); + + MQTT_TRC("[CID %p]:[State 0x%02x]: >> %s Topic size 0x%08x, Data size 0x%08x", + p_client, + p_client->state, + __func__, + p_param->message.topic.topic.utf_strlen, + p_param->message.payload.bin_strlen); + + MQTT_MUTEX_LOCK(); + + p_payload = &p_client->p_packet[MQTT_FIXED_HEADER_EXTENDED_SIZE]; + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_PENDING_WRITE)) + { + err_code = (NRF_ERROR_BUSY | IOT_MQTT_ERR_BASE); + } + else if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_CONNECTED)) + { + memset(p_payload, 0, MQTT_MAX_PACKET_LENGTH); + + // Pack topic. + err_code = pack_utf8_str(&p_param->message.topic.topic, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + + if (err_code == NRF_SUCCESS) + { + if (p_param->message.topic.qos) + { + err_code = pack_uint16(p_param->message_id, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + } + } + if (err_code == NRF_SUCCESS) + { + // Pack message on the topic. + err_code = pack_bin_str(&p_param->message.payload, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + } + + + if (err_code == NRF_SUCCESS) + { + const uint8_t message_type = MQTT_MESSAGES_OPTIONS(MQTT_PKT_TYPE_PUBLISH, + 0, // Duplicate flag not set. + p_param->message.topic.qos, + 0); // Retain flag not set. + + mqtt_packetlen = mqtt_encode_fixed_header(message_type, // Message type + offset, // Payload size without the fixed header + &p_payload); // Address where the p_payload is contained. + + + // Publish message. + err_code = mqtt_transport_write(p_client, p_payload, mqtt_packetlen); + } + } + + MQTT_TRC("<< %s", (uint32_t)__func__); + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +/**@brief Encodes and sends messages that contain only message id in the variable header. + * + * @param[in] p_client Identifies the client for which the procedure is requested. + * @param[in] op_code Opcode for the message. + * @param[in] message_id Message id to be encoded in the variable header. + * + * @retval NRF_SUCCESS or an error code indicating a reason for failure. + */ +uint32_t mqtt_message_id_only_enc_n_send(mqtt_client_t * const p_client, + uint8_t opcode, + uint16_t message_id) +{ + uint32_t err_code = MQTT_ERR_NOT_CONNECTED; + uint32_t offset = 0; + uint32_t mqtt_packetlen = 0; + uint8_t * p_payload; + + p_payload = &p_client->p_packet[MQTT_FIXED_HEADER_EXTENDED_SIZE]; + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_PENDING_WRITE)) + { + err_code = (NRF_ERROR_BUSY | IOT_MQTT_ERR_BASE); + } + else if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_CONNECTED)) + { + memset(p_payload, 0, MQTT_MAX_PACKET_LENGTH); + + err_code = pack_uint16(message_id, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + + if (err_code == NRF_SUCCESS) + { + const uint8_t message_type = MQTT_MESSAGES_OPTIONS(opcode, + 0, // Duplicate flag not set. + 0, // QoS unused. + 0); // Retain flag not set. + + mqtt_packetlen = mqtt_encode_fixed_header(message_type, // Message type + offset, // Payload size without the fixed header + &p_payload); // Address where the p_payload is contained. + + // Publish message. + err_code = mqtt_transport_write(p_client, p_payload, mqtt_packetlen); + } + } + + return err_code; +} + + +/**@brief Sends raw message to the peer. + * + * @param[in] p_client Identifies the client for which the procedure is requested. + * @param[in] p_message Raw message to be sent to the peer. + * @param[in] message_id Message id to be encoded in the variable header. + * + * @retval NRF_SUCCESS or an error code indicating a reason for failure. + */ +uint32_t mqtt_raw_message_send(mqtt_client_t * const p_client, + const uint8_t * p_message, + uint16_t message_len) +{ + uint32_t err_code; + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_PENDING_WRITE)) + { + err_code = (NRF_ERROR_BUSY | IOT_MQTT_ERR_BASE); + } + else if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_CONNECTED)) + { + err_code = mqtt_transport_write(p_client, p_message, message_len); + } + else + { + err_code = MQTT_ERR_NOT_CONNECTED; + } + + return err_code; +} + + +uint32_t mqtt_publish_ack(mqtt_client_t * const p_client, + mqtt_puback_param_t const * p_param) +{ + NULL_PARAM_CHECK(p_client); + NULL_PARAM_CHECK(p_param); + + MQTT_TRC("[CID %p]:[State 0x%02x]: >> %s Message id 0x%04x", + p_client, + p_client->state, + __func__, + p_param->message_id); + + MQTT_MUTEX_LOCK(); + + uint32_t err_code = mqtt_message_id_only_enc_n_send(p_client, + MQTT_PKT_TYPE_PUBACK, + p_param->message_id); + + MQTT_TRC("[CID %p]:[State 0x%02x]: << %s result 0x%08x", + p_client, + p_client->state, + __func__, + err_code); + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t mqtt_publish_receive(mqtt_client_t * const p_client, + mqtt_pubrec_param_t const * const p_param) +{ + NULL_PARAM_CHECK(p_client); + NULL_PARAM_CHECK(p_param); + + MQTT_TRC("[CID %p]:[State 0x%02x]: >> %s Message id 0x%04x", + p_client, + p_client->state, + __func__, + p_param->message_id); + + MQTT_MUTEX_LOCK(); + + uint32_t err_code = mqtt_message_id_only_enc_n_send(p_client, + MQTT_PKT_TYPE_PUBREC, + p_param->message_id); + + MQTT_TRC("[CID %p]:[State 0x%02x]: << %s result 0x%08x", + p_client, + p_client->state, + __func__, + err_code); + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t mqtt_publish_release(mqtt_client_t * const p_client, + mqtt_pubrel_param_t const * const p_param) +{ + NULL_PARAM_CHECK(p_client); + NULL_PARAM_CHECK(p_param); + + MQTT_TRC("[CID %p]:[State 0x%02x]: >> %s Message id 0x%04x", + p_client, + p_client->state, + __func__, + p_param->message_id); + + MQTT_MUTEX_LOCK(); + + uint32_t err_code = mqtt_message_id_only_enc_n_send(p_client, + MQTT_PKT_TYPE_PUBREL, + p_param->message_id); + + MQTT_TRC("[CID %p]:[State 0x%02x]: << %s result 0x%08x", + p_client, + p_client->state, + __func__, + err_code); + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t mqtt_publish_complete(mqtt_client_t * const p_client, + mqtt_pubcomp_param_t const * const p_param) +{ + NULL_PARAM_CHECK(p_client); + NULL_PARAM_CHECK(p_param); + + MQTT_TRC("[CID %p]:[State 0x%02x]: >> %s Message id 0x%04x", + p_client, + p_client->state, + __func__, + p_param->message_id); + + MQTT_MUTEX_LOCK(); + + uint32_t err_code = mqtt_message_id_only_enc_n_send(p_client, + MQTT_PKT_TYPE_PUBCOMP, + p_param->message_id); + + MQTT_TRC("[CID %p]:[State 0x%02x]: << %s result 0x%08x", + p_client, + p_client->state, + __func__, + err_code); + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t mqtt_disconnect(mqtt_client_t * const p_client) +{ + uint32_t err_code = MQTT_ERR_NOT_CONNECTED; + + NULL_PARAM_CHECK(p_client); + + MQTT_MUTEX_LOCK(); + + err_code = mqtt_raw_message_send(p_client, m_disc_packet, MQTT_FIXED_HEADER_SIZE); + + if (err_code == NRF_SUCCESS) + { + MQTT_SET_STATE_EXCLUSIVE(p_client, MQTT_STATE_DISCONNECTING); + } + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t mqtt_subscribe(mqtt_client_t * const p_client, + mqtt_subscription_list_t const * const p_param) +{ + uint32_t err_code = MQTT_ERR_NOT_CONNECTED; + uint32_t offset = 0; + uint32_t count = 0; + uint32_t mqtt_packetlen = 0; + uint8_t * p_payload; + + NULL_PARAM_CHECK(p_client); + NULL_PARAM_CHECK(p_param); + + MQTT_TRC("[CID %p]:[State 0x%02x]: >> %s message id 0x%04x topic count 0x%04x", + p_client, + p_client->state, + __func__, + p_param->message_id, + p_param->list_count); + + MQTT_MUTEX_LOCK(); + + p_payload = &p_client->p_packet[MQTT_FIXED_HEADER_EXTENDED_SIZE]; + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_PENDING_WRITE)) + { + err_code = (NRF_ERROR_BUSY | IOT_MQTT_ERR_BASE); + } + else if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_CONNECTED)) + { + memset(p_payload, 0, MQTT_MAX_PACKET_LENGTH); + + err_code = pack_uint16(p_param->message_id, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + + if (err_code == NRF_SUCCESS) + { + do + { + err_code = pack_utf8_str(&p_param->p_list[count].topic, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + if (err_code == NRF_SUCCESS) + { + err_code = pack_uint8(p_param->p_list[count].qos, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + } + count++; + } while ((err_code != NRF_SUCCESS) || (count < p_param->list_count)); + } + + if (err_code == NRF_SUCCESS) + { + const uint8_t message_type = MQTT_MESSAGES_OPTIONS(MQTT_PKT_TYPE_SUBSCRIBE, 0, 1, 0); + + // Rewind the packet to encode the packet correctly. + mqtt_packetlen = mqtt_encode_fixed_header(message_type, // Message type, Duplicate Flag, QoS and retain flag setting. + offset, // p_payload size without the fixed header + &p_payload); // Address where the p_payload is contained. Header will encoded by rewinding the location. + // Send message. + err_code = mqtt_transport_write(p_client, p_payload, mqtt_packetlen); + } + } + + MQTT_TRC("[CID %p]:[State 0x%02x]: << %s result 0x%08x", + p_client, + p_client->state, + __func__, + err_code); + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t mqtt_unsubscribe(mqtt_client_t * const p_client, + mqtt_subscription_list_t const * const p_param) +{ + uint32_t err_code = MQTT_ERR_NOT_CONNECTED; + uint32_t count = 0; + uint32_t offset = 0; + uint32_t mqtt_packetlen = 0; + uint8_t * p_payload; + + NULL_PARAM_CHECK(p_client); + NULL_PARAM_CHECK(p_param); + + MQTT_MUTEX_LOCK(); + + p_payload = &p_client->p_packet[MQTT_FIXED_HEADER_EXTENDED_SIZE]; + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_PENDING_WRITE)) + { + err_code = (NRF_ERROR_BUSY | IOT_MQTT_ERR_BASE); + } + else if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_CONNECTED)) + { + memset(p_payload, 0, MQTT_MAX_PACKET_LENGTH); + + err_code = pack_uint16(p_param->message_id, + MQTT_MAX_PACKET_LENGTH, + p_payload, + &offset); + + if (err_code == NRF_SUCCESS) + { + do + { + err_code = pack_utf8_str(&p_param->p_list[count].topic, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + count++; + } while ((err_code != NRF_SUCCESS) || (count < p_param->list_count)); + } + + if (err_code == NRF_SUCCESS) + { + const uint8_t message_type = MQTT_MESSAGES_OPTIONS(MQTT_PKT_TYPE_UNSUBSCRIBE, + 0, // Duplicate flag. + MQTT_QoS_1_ATLEAST_ONCE, + 0); // Retain flag. + + // Rewind the packet to encode the packet correctly. + mqtt_packetlen = mqtt_encode_fixed_header(message_type, // Message type, Duplicate Flag, QoS and retain flag setting. + offset, // Payload size without the fixed header + &p_payload); // Address where the p_payload is contained. Header will encoded by rewinding the location. + + // Send message. + err_code = mqtt_transport_write(p_client, p_payload, mqtt_packetlen); + } + } + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t mqtt_ping(mqtt_client_t * const p_client) +{ + uint32_t err_code; + + NULL_PARAM_CHECK(p_client); + + MQTT_MUTEX_LOCK(); + + err_code = mqtt_raw_message_send(p_client, m_ping_packet, MQTT_PKT_HEADER_SIZE); + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t mqtt_abort(mqtt_client_t * const p_client) +{ + MQTT_MUTEX_LOCK(); + + NULL_PARAM_CHECK(p_client); + + if (p_client->state != MQTT_STATE_IDLE) + { + mqtt_client_tcp_abort(p_client); + } + + MQTT_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t mqtt_live(void) +{ + iot_timer_time_in_ms_t elapsed_time; + uint32_t index; + + // Note: The module should not be locked when calling this TLS API. + nrf_tls_process(); + + MQTT_MUTEX_LOCK(); + + for (index = 0; index < MQTT_MAX_CLIENTS; index++) + { + mqtt_client_t * p_client = m_mqtt_client[index]; + if (p_client != NULL) + { + UNUSED_VARIABLE(iot_timer_wall_clock_delta_get(&p_client->last_activity, + &elapsed_time)); + + if ((MQTT_KEEPALIVE > 0) && (elapsed_time > ((MQTT_KEEPALIVE - 2) * 1000))) + { + UNUSED_VARIABLE(mqtt_ping(p_client)); + } + if (p_client->p_pending_packet != NULL) + { + uint32_t err; + err = mqtt_transport_write(p_client, p_client->p_pending_packet, + p_client->pending_packetlen); + + if (err == NRF_SUCCESS) + { + p_client->p_pending_packet = NULL; + p_client->pending_packetlen = 0; + } + } + } + } + + MQTT_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t mqtt_input(mqtt_client_t * p_client, uint32_t timeout) +{ + uint32_t err_code; + + NULL_PARAM_CHECK(p_client); + + MQTT_MUTEX_LOCK(); + + MQTT_TRC("%s: 0x%08x", __func__, p_client->state); + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_TCP_CONNECTED) || + MQTT_VERIFY_STATE(p_client, MQTT_STATE_DISCONNECTING)) + { + err_code = tcp_receive_packet(p_client, timeout); + } + else + { + err_code = (NRF_ERROR_INVALID_STATE | IOT_MQTT_ERR_BASE); + } + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt.h new file mode 100644 index 0000000..980426a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt.h @@ -0,0 +1,506 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file mqtt.h + * + * @defgroup iot_sdk_mqtt_api MQTT Client on nRF5x + * @ingroup iot_sdk_mqtt + * @{ + * @brief MQTT Client Implementation on the Nordic nRF platforms. + * + * @details + * MQTT Client's Application interface is defined in this header. + * + * @note The implementation assumes LwIP Stack is available with TCP module enabled. + * + * @note By default the implementation uses MQTT version 3.1.0. + * However few cloud services like the Xively use the version 3.1.1. + * For this please set p_client.protocol_version = MQTT_VERSION_3_1_1. + */ + +#ifndef MQTT_H_ +#define MQTT_H_ + +#include <stdint.h> +#include "iot_defines.h" +#include "iot_timer.h" +#include "nrf_tls.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief MQTT Asynchronous Events notified to the application from the module + * through the callback registered by the application. */ +typedef enum +{ + MQTT_EVT_CONNACK, /**< Acknowledgment of connection request. Event result accompanying the event indicates whether the connection failed or succeeded. */ + MQTT_EVT_DISCONNECT, /**< Disconnection Event. MQTT Client Reference is no longer valid once this event is received for the client. */ + MQTT_EVT_PUBLISH, /**< Publish event received when message is published on a topic client is subscribed to. */ + MQTT_EVT_PUBACK, /**< Acknowledgment for published message with QoS 1. */ + MQTT_EVT_PUBREC, /**< Reception confirmation for published message with QoS 2. */ + MQTT_EVT_PUBREL, /**< Release of published published messages with QoS 2. */ + MQTT_EVT_PUBCOMP, /**< Confirmation to a publish release message. Applicable only to QoS 2 messages. */ + MQTT_EVT_SUBACK, /**< Acknowledgment to a subscription request. */ + MQTT_EVT_UNSUBACK /**< Acknowledgment to a unsubscription request. */ +} mqtt_evt_id_t; + +/**@brief MQTT version protocol level. */ +typedef enum +{ + MQTT_VERSION_3_1_0 = 3, /**< Protocol level for 3.1.0. */ + MQTT_VERSION_3_1_1 = 4 /**< Protocol level for 3.1.1. */ +} mqtt_version_t; + +/**@brief MQTT transport type. */ +typedef enum +{ + MQTT_TRANSPORT_NON_SECURE = 0x00, /**< Use non secure TCP transport for MQTT connection. */ + MQTT_TRANSPORT_SECURE = 0x01, /**< Use secure TCP transport (TLS) for MQTT connection. */ + MQTT_TRANSPORT_MAX = 0x02 /**< Shall not be used as a transport type. Indicator of maximum transport types possible. */ +} mqtt_transport_type_t; + +/**@brief MQTT Quality of Service types. */ +typedef enum +{ + MQTT_QoS_0_AT_MOST_ONCE = 0x00, /**< Lowest Quality of Service, no acknowledgment needed for published message. */ + MQTT_QoS_1_ATLEAST_ONCE = 0x01, /**< Medium Quality of Service, if acknowledgment expected for published message, duplicate messages permitted. */ + MQTT_QoS_2_EACTLY_ONCE = 0x02 /**< Highest Quality of Service, acknowledgment expected and message shall be published only once. Message not published to interested parties unless client issues a PUBREL. */ +} mqtt_qos_t; + +/**@brief MQTT Asynchronous Events notified to the application from the module + * through the callback registered by the application. */ +typedef enum +{ + MQTT_CONNECTION_ACCEPTED = 0x00, /**< Connection accepted. */ + MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 0x01, /**< The Server does not support the level of the MQTT protocol requested by the Client. */ + MQTT_IDENTIFIER_REJECTED = 0x02, /**< The Client identifier is correct UTF-8 but not allowed by the Server. */ + MQTT_SERVER_UNAVAILABLE = 0x03, /**< The Network Connection has been made but the MQTT service is unavailable. */ + MQTT_BAD_USER_NAME_OR_PASSWORD = 0x04, /**< The data in the user name or password is malformed. */ + MQTT_NOT_AUTHORIZED = 0x05 /**< The Client is not authorized to connect. */ +} mqtt_conn_return_code_t; + +/**@brief MQTT client forward declaration @ref mqtt_client_t for details. */ +typedef struct mqtt_client_t mqtt_client_t; + +/**@brief Abstracts UTF-8 encoded strings. */ +typedef struct +{ + uint8_t * p_utf_str; /**< Pointer to UTF-8 string. */ + uint32_t utf_strlen; /**< Length of UTF string. */ +} mqtt_utf8_t; + +/**@brief Abstracts binary strings. */ +typedef struct +{ + uint8_t * p_bin_str; /**< Pointer to binary stream. */ + uint32_t bin_strlen; /**< Length of binary stream. */ +} mqtt_binstr_t; + +/**@brief Abstracts MQTT UTF-8 encoded topic that can be subscribed to or published. */ +typedef struct +{ + mqtt_utf8_t topic; /**< Topic on to be published or subscribed to. */ + uint8_t qos; /**< Quality of service requested for the subscription. @ref mqtt_qos_t for details. */ +} mqtt_topic_t; + +/**@brief Abstracts MQTT UTF-8 encoded unique client identifier. */ +typedef mqtt_utf8_t mqtt_client_id_t; + +/**@brief Abstracts MQTT UTF-8 encoded password to be used for the client connection. */ +typedef mqtt_utf8_t mqtt_password_t; + +/**@brief Abstracts MQTT UTF-8 encoded user name to be used for the client connection. */ +typedef mqtt_utf8_t mqtt_username_t; + +/**@brief Abstracts will message used in @ref mqtt_connect request. + * + * @note utf8 is used here instead of binary string as a zero length encoding is expected in + * will message is empty. + */ +typedef mqtt_utf8_t mqtt_will_message_t; + +/**@brief Abstracts message in binary encoded string received or published on a topic. */ +typedef mqtt_binstr_t mqtt_message_t; + +/**@brief Parameters for a publish message. */ +typedef struct +{ + mqtt_topic_t topic; /**< Topic on which data was published. */ + mqtt_message_t payload; /**< Payload on the topic published. */ +} mqtt_publish_message_t; + +/**@brief Parameters for a connection acknowledgment (connack). */ +typedef struct +{ + uint8_t session_present_flag; /**< The Session Present flag enables a Client to establish whether the Client and Server have a consistent view about whether there is already stored Session state. */ + mqtt_conn_return_code_t return_code; /**< The appropriate non-zero Connect return code indicates if the Server is unable to process a connection request for some reason. */ +} mqtt_connack_param_t; + +/**@brief Parameters for MQTT publish acknowledgment(puback). */ +typedef struct +{ + uint16_t message_id; +} mqtt_puback_param_t; + +/**@brief Parameters for MQTT publish receive(pubrec). */ +typedef struct +{ + uint16_t message_id; +} mqtt_pubrec_param_t; + +/**@brief Parameters for MQTT publish release(pubrec). */ +typedef struct +{ + uint16_t message_id; +} mqtt_pubrel_param_t; + +/**@brief Parameters for MQTT publish complete(pubcomp). */ +typedef struct +{ + uint16_t message_id; +} mqtt_pubcomp_param_t; + +/**@brief Parameters for MQTT subscription acknowledgment (suback). */ +typedef struct +{ + uint16_t message_id; +} mqtt_suback_param_t; + +/**@brief Parameters for MQTT unsubscription acknowledgment (unsuback). */ +typedef struct +{ + uint16_t message_id; +} mqtt_unsuback_param_t; + +/**@brief Parameters for a publish message. */ +typedef struct +{ + mqtt_publish_message_t message; /**< Messages including topic, QoS and its payload (if any) to be published. */ + uint16_t message_id; /**< Message id used for the publish message. Redundant for QoS 0. */ + uint8_t dup_flag:1; /**< Duplicate flag. If 1, it indicates the message is being retransmitted. Has no meaning with QoS 0. */ + uint8_t retain_flag:1; /**< retain flag. If 1, the message shall be stored persistently by the broker. */ +} mqtt_publish_param_t; + +/**@brief List of topics in a subscription request. */ +typedef struct +{ + mqtt_topic_t * p_list; /**< Array containing topics along with QoS for each. */ + uint32_t list_count; /**< Number of topics in the subscription list */ + uint16_t message_id; /**< Message id used to identify subscription request. */ +} mqtt_subscription_list_t; + +/** + * @brief Defines event parameters notified along with asynchronous events to the application. + * Currently, only MQTT_EVT_PUBLISH is accompanied with parameters. + */ +typedef union +{ + mqtt_connack_param_t connack; /**< Parameters accompanying MQTT_EVT_CONNACK event. */ + mqtt_publish_param_t publish; /**< Parameters accompanying MQTT_EVT_PUBLISH event. */ + mqtt_puback_param_t puback; /**< Parameters accompanying MQTT_EVT_PUBACK event. */ + mqtt_pubrec_param_t pubrec; /**< Parameters accompanying MQTT_EVT_PUBREC event. */ + mqtt_pubrel_param_t pubrel; /**< Parameters accompanying MQTT_EVT_PUBREL event. */ + mqtt_pubcomp_param_t pubcomp; /**< Parameters accompanying MQTT_EVT_PUBCOMP event. */ + mqtt_suback_param_t suback; /**< Parameters accompanying MQTT_EVT_SUBACK event. */ + mqtt_suback_param_t unsuback; /**< Parameters accompanying MQTT_EVT_UNSUBACK event. */ +} mqtt_evt_param_t; + +/**@brief Defined MQTT asynchronous event notified to the application. */ +typedef struct +{ + mqtt_evt_id_t id; /**< Identifies the event. */ + mqtt_evt_param_t param; /**< Contains parameters (if any) accompanying the event. */ + uint32_t result; /**< Event result. For example, MQTT_EVT_CONNACK has a result code indicating success or failure code of connection procedure. */ +} mqtt_evt_t; + +/**@brief Asynchronous event notification callback registered by the application with + * the module to receive module events. + * + * @param[in] p_client Identifies the client for which the event is notified. + * @param[in] p_evet Event description along with result and associated parameters (if any). + */ +typedef void (*mqtt_evt_cb_t)(mqtt_client_t * const p_client, const mqtt_evt_t * p_evt); + +/**@brief MQTT Client definition to maintain information relevant to the client. */ +struct mqtt_client_t +{ + mqtt_client_id_t client_id; /**< Unique client identification to be used for the connection. Shall be zero length or NULL valued. */ + mqtt_username_t * p_user_name; /**< User name (if any) to be used for the connection. NULL indicates no user name. */ + mqtt_password_t * p_password; /**< Password (if any) to be used for the connection. Note that if password is provided, user name shall also be provided. NULL indicates no password. */ + mqtt_topic_t * p_will_topic; /**< Will topic and QoS. Can be NULL. */ + mqtt_will_message_t * p_will_message; /**< Will message. Can be NULL. Non NULL value valid only if will topic is not NULL. */ + nrf_tls_key_settings_t * p_security_settings; /**< Provide security settings like PSK, own certificate etc here. The memory provided for the settings shall be resident. */ + mqtt_evt_cb_t evt_cb; /**< Application callback registered with the module to get MQTT events. */ + ipv6_addr_t broker_addr; /**< IPv6 Address of MQTT broker to which client connection is requested. */ + uint16_t broker_port; /**< Broker's Port number. */ + uint8_t poll_abort_counter; /**< Poll abort counter maintained for the TCP connection. */ + uint8_t protocol_version; /**< MQTT protocol version. */ + uint8_t transport_type; /**< Transport type selection for client instance. @ref mqtt_transport_type_t for possible values. MQTT_TRANSPORT_MAX is not a valid type.*/ + uint8_t will_retain:1; /**< Will retain flag, 1 if will message shall be retained persistently. */ + uint8_t clean_session:1; /**< Clean session flag indicating a fresh (1) or a retained session (0). Default is 1. */ + iot_timer_time_in_ms_t last_activity; /**< Internal. Ticks maintaining wallcock in last activity that occurred. Needed for periodic PING. */ + uint32_t state; /**< Internal. Shall not be touched by the application. Client's state in the connection. */ + int socket_fd; /**< Internal. Shall not be touched by the application. TCP socket file descriptor. */ + uint32_t tcp_id; /**< Internal. Shall not be touched by the application. TCP Connection Reference provided by the IP stack. */ + uint8_t * p_packet; /**< Internal. Shall not be touched by the application. Used for creating MQTT packet in TX path. */ + uint8_t * p_pending_packet; /**< Internal. Shall not be touched by the application. */ + nrf_tls_instance_t tls_instance; /**< Internal. Shall not be touched by the application. TLS instance identifier. Valid only if transport is a secure one. */ + uint32_t pending_packetlen; /**< Internal. Shall not be touched by the application. */ +}; + + +/** + * @brief Initializes the module. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + * + * @note Shall be called before initiating any procedures on the module. + * @note If module initialization fails, no module APIs shall be called. + */ +uint32_t mqtt_init(void); + + +/** + * @brief Initializes the client instance. + * + * @param[in] p_client Client instance for which the procedure is requested. + * Shall not be NULL. + * + * @note Shall be called before connecting the client in order to avoid unexpected behavior + * caused by uninitialized parameters. + */ +void mqtt_client_init(mqtt_client_t * const p_client); + + +/** + * @brief API to request new MQTT client connection. + * + * @param[out] p_client Client instance for which the procedure is requested. + * Shall not be NULL. + * + * @note This memory is assumed to be resident until mqtt_disconnect is called. + * @note Any subsequent changes to parameters like broker address, user name, device id, etc. have + * no effect once MQTT connection is established. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + * + * @note Default protocol revision used for connection request is 3.1.0. Please set + * p_client.protocol_version = MQTT_VERSION_3_1_1 to use protocol 3.1.1. + * @note If more than one simultaneous client connections are needed, please define + * MQTT_MAX_CLIENTS to override default of 1. + * @note Please define MQTT_KEEPALIVE time to override default of 1 minute. + * @note Please define MQTT_MAX_PACKET_LENGTH time to override default of 128 bytes. + * Ensure the system has enough memory for the new length per client. + */ +uint32_t mqtt_connect(mqtt_client_t * const p_client); + + +/** + * @brief API to publish messages on topics. + * + * @param[in] p_client Client instance for which the procedure is requested. + * Shall not be NULL. + * @param[in] p_param Parameters to be used for the publish message. + * Shall not be NULL. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + * + * @note Default protocol revision used for connection request is 3.1.0. Please set + * p_client.protocol_version = MQTT_VERSION_3_1_1 to use protocol 3.1.1. + */ +uint32_t mqtt_publish(mqtt_client_t * const p_client, + mqtt_publish_param_t const * const p_param); + + +/** + * @brief API used by subscribing client to send acknowledgment to the broker. + * Applicable only to QoS 1 publish messages. + * + * @param[in] p_client Client instance for which the procedure is requested. + * Shall not be NULL. + * @param[in] p_param Identifies message being acknowledged. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + * + * @note Default protocol revision used for connection request is 3.1.0. Please set + * p_client.protocol_version = MQTT_VERSION_3_1_1 to use protocol 3.1.1. + */ +uint32_t mqtt_publish_ack(mqtt_client_t * const p_client, + mqtt_puback_param_t const * const p_param); + + +/** + * @brief API to send assured acknowledgment from a subscribing client to the broker. + * Should be called on reception of @ref MQTT_EVT_PUBLISH with QoS set to + * @ref MQTT_QoS_2_EACTLY_ONCE. + * + * @param[in] p_client Identifies client instance for which the procedure is requested. + * Shall not be NULL. + * @param[in] p_param Identifies message being acknowledged. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + * + * @note Default protocol revision used for connection request is 3.1.0. Please set + * p_client.protocol_version = MQTT_VERSION_3_1_1 to use protocol 3.1.1. + */ +uint32_t mqtt_publish_receive(mqtt_client_t * const p_client, + mqtt_pubrec_param_t const * const p_param); + + +/** + * @brief API to used by publishing client to request releasing published data. + * Shall be used only after @ref MQTT_EVT_PUBREC is received and is valid + * only for QoS level @ref MQTT_QoS_2_EACTLY_ONCE. + * + * @param[in] p_client Client instance for which the procedure is requested. + * Shall not be NULL. + * @param[in] p_param Identifies message being released. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + * + * @note Default protocol revision used for connection request is 3.1.0. Please set + * p_client.protocol_version = MQTT_VERSION_3_1_1 to use protocol 3.1.1. + */ +uint32_t mqtt_publish_release(mqtt_client_t * const p_client, + mqtt_pubrel_param_t const * const p_param); + + +/** + * @brief API used by subscribing clients to acknowledge reception of a released message. + * Should be used on reception @ref MQTT_EVT_PUBREL event. + * + * @param[in] p_client Identifies client instance for which the procedure is requested. + * Shall not be NULL. + * @param[in] p_param Identifies message being completed. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + * + * @note Default protocol revision used for connection request is 3.1.0. Please set + * p_client.protocol_version = MQTT_VERSION_3_1_1 to use protocol 3.1.1. + */ +uint32_t mqtt_publish_complete(mqtt_client_t * const p_client, + mqtt_pubcomp_param_t const * const p_param); + + +/** + * @brief API to request subscribing to a topic on the connection. + * + * @param[in] p_client Identifies client instance for which the procedure is requested. + * Shall not be NULL. + * @param[in] p_param Subscription parameters. Shall not be NULL. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_subscribe(mqtt_client_t * const p_client, + mqtt_subscription_list_t const * const p_param); + + +/** + * @brief API to request un-subscribe from a topic on the connection. + * + * @param[in] p_client Identifies client instance for which the procedure is requested. + * Shall not be NULL. + * @param[in] p_param Parameters describing topics being unsubscribed from. + * Shall not be NULL. + * + * @note QoS included in topic description is unused in this API. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_unsubscribe(mqtt_client_t * const p_client, + mqtt_subscription_list_t const * const p_param); + + +/** + * @brief API to abort MQTT connection. + * + * @param[in] p_client Identifies client instance for which procedure is requested. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_abort(mqtt_client_t * const p_client); + + +/** + * @brief API to disconnect MQTT connection. + * + * @param[in] p_client Identifies client instance for which procedure is requested. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_disconnect(mqtt_client_t * const p_client); + + +/** + * @brief This API should be called periodically for the module to be able to keep the connection + * alive by sending Ping Requests if need be. + * + * @note Application shall ensure that the periodicity of calling this function makes it possible to + * respect the Keep Alive time agreed with the broker on connection. + * @ref mqtt_connect for details on Keep Alive time. + * + * @retval NRF_SUCCESS or an result code indicating reason for failure. + */ +uint32_t mqtt_live(void); + + +/** + * @brief Wait for an incoming MQTT packet. + * The registered callback will be called with the packet payload. + * + * @param[in] p_client Client instance for which the procedure is requested. + * Shall not be NULL. + * @param[in] timeout Maximum interval (in milliseconds) to wait for a packet. + * If timeout is 0, the interval is indefinitely. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + * + * @note This API is only supported when using the socket transport layer. + */ +uint32_t mqtt_input(mqtt_client_t * const p_client, uint32_t timeout); + + +#ifdef __cplusplus +} +#endif + +#endif // MQTT_H_ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_decoder.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_decoder.c new file mode 100644 index 0000000..4a95865 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_decoder.c @@ -0,0 +1,262 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file mqtt_decoder.c + * + * @brief Decoder functions needs for decoding packets received from the broker. + */ + +#include "mqtt_internal.h" + +#if MQTT_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME mqtt_dec + +#define NRF_LOG_LEVEL MQTT_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR MQTT_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR MQTT_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define MQTT_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define MQTT_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define MQTT_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define MQTT_ENTRY() MQTT_TRC(">> %s", __func__) +#define MQTT_EXIT() MQTT_TRC("<< %s", __func__) + +#else // MQTT_CONFIG_LOG_ENABLED + +#define MQTT_TRC(...) /**< Disables traces. */ +#define MQTT_DUMP(...) /**< Disables dumping of octet streams. */ +#define MQTT_ERR(...) /**< Disables error logs. */ + +#define MQTT_ENTRY(...) +#define MQTT_EXIT(...) + +#endif // MQTT_CONFIG_LOG_ENABLED + +#define MQTT_LENGTH_VALUE_MASK 0x7F +#define MQTT_LENGTH_CONTINUATION_BIT 0x80 +#define MQTT_LENGTH_MULTIPLIER 0x80 + + +uint32_t unpack_uint8(uint8_t * p_val, + uint32_t buffer_len, + uint8_t * const buffer, + uint32_t * const p_offset) +{ + uint32_t err_code = NRF_ERROR_DATA_SIZE; + + if (buffer_len > (*p_offset)) + { + const uint32_t available_len = buffer_len - (*p_offset); + + MQTT_TRC(">> %s BL:%08x, B:%p, O:%08x A:%08x", __func__, + buffer_len, buffer, (*p_offset), available_len); + + if (available_len >= SIZE_OF_UINT8) + { + // Create unit8 value. + (*p_val) = buffer[*p_offset]; + + // Increment offset. + (*p_offset) += SIZE_OF_UINT8; + + // Indicate success. + err_code = NRF_SUCCESS; + } + } + + MQTT_TRC("<< %s result:0x%08x val:0x%02x", __func__, err_code, (*p_val)); + return err_code; +} + + +uint32_t unpack_uint16(uint16_t * p_val, + uint32_t buffer_len, + uint8_t * const buffer, + uint32_t * const p_offset) +{ + uint32_t err_code = NRF_ERROR_DATA_SIZE; + + if (buffer_len > (*p_offset)) + { + const uint32_t available_len = buffer_len - (*p_offset); + + MQTT_TRC(">> %s BL:%08x, B:%p, O:%08x A:%08x", __func__, + buffer_len, buffer, (*p_offset), available_len); + + if (available_len >= SIZE_OF_UINT16) + { + // Create unit16 value. + (*p_val) = ((buffer[*p_offset] & 0x00FF) << 8); // MSB + (*p_val) |= (buffer[(*p_offset+1)] & 0x00FF); // LSB + + // Increment offset. + (*p_offset) += SIZE_OF_UINT16; + + // Indicate success. + err_code = NRF_SUCCESS; + } + } + + MQTT_TRC("<< %s result:0x%08x val:0x%04x", __func__, err_code, (*p_val)); + + return err_code; +} + + +uint32_t unpack_utf8_str(mqtt_utf8_t * const p_str, + uint32_t buffer_len, + uint8_t * const buffer, + uint32_t * const p_offset) +{ + uint16_t utf8_strlen; + uint32_t err_code = unpack_uint16(&utf8_strlen, buffer_len, buffer, p_offset); + + p_str->p_utf_str = NULL; + p_str->utf_strlen = 0; + + if (err_code == NRF_SUCCESS) + { + const uint32_t available_len = buffer_len - (*p_offset); + + MQTT_TRC(">> %s BL:%08x, B:%p, O:%08x A:%08x", __func__, + buffer_len, buffer, (*p_offset), available_len); + + if (utf8_strlen <= available_len) + { + // Zero length UTF8 strings permitted. + if (utf8_strlen) + { + // Point to right location in buffer. + p_str->p_utf_str = &buffer[(*p_offset)]; + } + + // Populate length field. + p_str->utf_strlen = utf8_strlen; + + // Increment offset. + (*p_offset) += utf8_strlen; + + // Indicate success + err_code = NRF_SUCCESS; + } + else + { + // Reset to original value. + (*p_offset) -= SIZE_OF_UINT16; + + err_code = NRF_ERROR_DATA_SIZE; + } + } + + MQTT_TRC("<< %s result:0x%08x utf8 len:0x%08x", __func__, err_code, p_str->utf_strlen); + + return err_code; +} + + +uint32_t unpack_bin_str(mqtt_binstr_t * const p_str, + uint32_t buffer_len, + uint8_t * const buffer, + uint32_t * const p_offset) +{ + uint32_t error_code = NRF_ERROR_DATA_SIZE; + + MQTT_TRC(">> %s BL:%08x, B:%p, O:%08x", __func__, + buffer_len, buffer, (*p_offset)); + + if (buffer_len >= (*p_offset)) + { + p_str->p_bin_str = NULL; + p_str->bin_strlen = 0; + + // Indicate success zero length binary strings are permitted. + error_code = NRF_SUCCESS; + + const uint32_t available_len = buffer_len - (*p_offset); + + if (available_len) + { + // Point to start of binary string. + p_str->p_bin_str = &buffer[(*p_offset)]; + p_str->bin_strlen = available_len; + + // Increment offset. + (*p_offset) += available_len; + } + } + + MQTT_TRC("<< %s bin len:0x%08x", __func__, p_str->bin_strlen); + + return error_code; +} + + +uint32_t packet_length_decode(uint8_t * p_buffer, + uint32_t buffer_len, + uint32_t * p_remaining_length, + uint32_t * p_offset) +{ + uint32_t index = (*p_offset); + uint32_t remaining_length = 0; + uint32_t multiplier = 1; + + do + { + if (index >= buffer_len) + { + return NRF_ERROR_DATA_SIZE; + } + + remaining_length += (p_buffer[index] & MQTT_LENGTH_VALUE_MASK) * multiplier; + multiplier *= MQTT_LENGTH_MULTIPLIER; + + } while ((p_buffer[index++] & MQTT_LENGTH_CONTINUATION_BIT) != 0); + + *p_offset = index; + *p_remaining_length = remaining_length; + + MQTT_TRC("%s: RL:0x%08x RLS:0x%08x", __func__, remaining_length, index); + + return NRF_SUCCESS; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_encoder.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_encoder.c new file mode 100644 index 0000000..74476f2 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_encoder.c @@ -0,0 +1,457 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file mqtt_encoder.c + * + * @brief Encoding functions needed to create packet to be sent to the broker. + */ + + +#include "mqtt_internal.h" + +#if MQTT_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME mqtt_enc + +#define NRF_LOG_LEVEL MQTT_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR MQTT_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR MQTT_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define MQTT_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define MQTT_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define MQTT_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define MQTT_ENTRY() MQTT_TRC(">> %s", __func__) +#define MQTT_EXIT() MQTT_TRC("<< %s", __func__) + +#else // MQTT_CONFIG_LOG_ENABLED + +#define MQTT_TRC(...) /**< Disables traces. */ +#define MQTT_DUMP(...) /**< Disables dumping of octet streams. */ +#define MQTT_ERR(...) /**< Disables error logs. */ + +#define MQTT_ENTRY(...) +#define MQTT_EXIT(...) + +#endif // MQTT_CONFIG_LOG_ENABLED + +#define MQTT_3_1_0_PROTO_DESC_LEN 6 +#define MQTT_3_1_1_PROTO_DESC_LEN 4 + +const uint8_t mqtt_3_1_0_proto_desc_str[MQTT_3_1_0_PROTO_DESC_LEN] = {'M', 'Q', 'I', 's', 'd', 'p'}; +const uint8_t mqtt_3_1_1_proto_desc_str[MQTT_3_1_1_PROTO_DESC_LEN] = {'M', 'Q', 'T', 'T'}; + +const mqtt_utf8_t mqtt_3_1_0_proto_desc = +{ + .p_utf_str = (uint8_t *)&mqtt_3_1_0_proto_desc_str[0], + .utf_strlen = MQTT_3_1_0_PROTO_DESC_LEN +}; + +const mqtt_utf8_t mqtt_3_1_1_proto_desc = +{ + .p_utf_str = (uint8_t *)&mqtt_3_1_1_proto_desc_str[0], + .utf_strlen = MQTT_3_1_1_PROTO_DESC_LEN +}; + +uint32_t pack_uint8(uint8_t val, + uint32_t buffer_len, + uint8_t * const buffer, + uint32_t * const p_offset) +{ + uint32_t err_code = NRF_ERROR_DATA_SIZE; + + if (buffer_len > (*p_offset)) + { + MQTT_TRC(">> %s V:%02x BL:%08x, B:%p, O:%08x", __func__, + val, buffer_len, buffer, (*p_offset)); + + // Pack value. + buffer[(*p_offset)] = val; + + // Increment offset. + (*p_offset) += SIZE_OF_UINT8; + + // Indicate success. + err_code = NRF_SUCCESS; + } + + return err_code; +} + + +uint32_t pack_uint16(uint16_t val, + uint32_t buffer_len, + uint8_t * const buffer, + uint32_t * const p_offset) +{ + uint32_t err_code = NRF_ERROR_DATA_SIZE; + + if (buffer_len > (*p_offset)) + { + const uint32_t available_len = buffer_len - (*p_offset); + + MQTT_TRC(">> %s V:%04x BL:%08x, B:%p, O:%08x A:%08x", __func__, + val, buffer_len, buffer, (*p_offset), available_len); + + if (available_len >= SIZE_OF_UINT16) + { + // Pack value. + buffer[(*p_offset)] = MSB_16(val); + buffer[(*p_offset)+1] = LSB_16(val); + + // Increment offset. + (*p_offset) += SIZE_OF_UINT16; + + // Indicate success. + err_code = NRF_SUCCESS; + } + } + + return err_code; +} + + +uint32_t pack_utf8_str(mqtt_utf8_t const * const p_str, + uint32_t buffer_len, + uint8_t * const buffer, + uint32_t * const p_offset) +{ + uint32_t err_code = NRF_ERROR_DATA_SIZE; + + if (buffer_len > (*p_offset)) + { + const uint32_t available_len = buffer_len - (*p_offset); + err_code = NRF_ERROR_NO_MEM; + + MQTT_TRC(">> %s USL:%08x BL:%08x, B:%p, O:%08x A:%08x", __func__, + GET_UT8STR_BUFFER_SIZE(p_str), buffer_len, buffer, (*p_offset), available_len); + + if (available_len >= GET_UT8STR_BUFFER_SIZE(p_str)) + { + // Length followed by string. + err_code = pack_uint16(p_str->utf_strlen, buffer_len, buffer, p_offset); + + if (err_code == NRF_SUCCESS) + { + memcpy(&buffer[(*p_offset)], p_str->p_utf_str, p_str->utf_strlen); + + (*p_offset) += p_str->utf_strlen; + + err_code = NRF_SUCCESS; + } + } + } + + return err_code; +} + +uint32_t pack_bin_str(mqtt_binstr_t const * const p_str, + uint32_t buffer_len, + uint8_t * const buffer, + uint32_t * const p_offset) +{ + uint32_t err_code = NRF_ERROR_DATA_SIZE; + + if (buffer_len > (*p_offset)) + { + const uint32_t available_len = buffer_len - (*p_offset); + err_code = NRF_ERROR_NO_MEM; + + MQTT_TRC(">> %s BSL:%08x BL:%08x, B:%p, O:%08x A:%08x", __func__, + GET_BINSTR_BUFFER_SIZE(p_str), buffer_len, buffer, (*p_offset), available_len); + + if (available_len >= GET_BINSTR_BUFFER_SIZE(p_str)) + { + memcpy(&buffer[(*p_offset)], p_str->p_bin_str, p_str->bin_strlen); + + (*p_offset) += p_str->bin_strlen; + err_code = NRF_SUCCESS; + } + } + + return err_code; +} + + +void packet_length_encode(uint32_t remaining_length, uint8_t * p_buff, uint32_t * p_size) +{ + uint16_t index = 0; + const uint32_t offset = (*p_size); + + MQTT_TRC(">> RL:0x%08x O:%08x P:%p", remaining_length, offset, p_buff); + + do + { + if (p_buff != NULL) + { + p_buff[offset+index] = remaining_length % 0x80; + } + + remaining_length /= 0x80; + + if (remaining_length > 0) + { + if (p_buff != NULL) + { + p_buff[offset+index] |= 0x80; + } + } + + index++; + + } while (remaining_length > 0); + + MQTT_TRC("<< RLS:0x%08x", index); + + *p_size += index; +} + + +uint32_t mqtt_encode_fixed_header(uint8_t message_type, uint32_t length, uint8_t ** pp_packet) +{ + uint32_t packet_length = 0xFFFFFFFF; + + if (MQTT_MAX_PAYLOAD_SIZE >= length) + { + uint32_t offset = 1; + + MQTT_TRC("<< %s MT:0x%02x L:0x%08x", __func__, message_type, length); + packet_length_encode(length, NULL, &offset); + + MQTT_TRC("Remaining length size = %02x", offset); + + uint8_t * p_mqtt_header = ((*pp_packet) - offset); + + // Reset offset. + offset = 0; + UNUSED_VARIABLE(pack_uint8(message_type, MQTT_MAX_PACKET_LENGTH, p_mqtt_header, &offset)); + packet_length_encode(length, p_mqtt_header, &offset); + + (* pp_packet) = p_mqtt_header; + + packet_length = (length + offset); + } + + return packet_length; +} + + +uint32_t zero_len_str_encode(uint32_t buffer_len, + uint8_t * const buffer, + uint32_t * const offset) +{ + return pack_uint16(0x0000, buffer_len, buffer, offset); +} + + +void connect_request_encode(const mqtt_client_t * p_client, + uint8_t ** pp_packet, + uint32_t * p_packet_length) +{ + uint32_t err_code; + uint32_t offset = 0; + uint8_t * p_payload = &p_client->p_packet[MQTT_FIXED_HEADER_EXTENDED_SIZE]; + uint8_t connect_flags = p_client->clean_session << 1; // Clean session always. + + const mqtt_utf8_t * p_mqtt_proto_desc; + if (p_client->protocol_version == MQTT_VERSION_3_1_1) + { + p_mqtt_proto_desc = &mqtt_3_1_1_proto_desc; + } + else + { + p_mqtt_proto_desc = &mqtt_3_1_0_proto_desc; + } + + memset(p_payload, 0, MQTT_MAX_PACKET_LENGTH); + + // Pack protocol description. + MQTT_TRC("Encoding Protocol Description. Str:%s Size:%08x.", + p_mqtt_proto_desc->p_utf_str, + p_mqtt_proto_desc->utf_strlen); + + err_code = pack_utf8_str(p_mqtt_proto_desc, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + + if (err_code == NRF_SUCCESS) + { + MQTT_TRC("Encoding Protocol Version %02x.", p_client->protocol_version); + // Pack protocol version. + err_code = pack_uint8(p_client->protocol_version, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + } + + // Remember position of connect flag and + // leave one byte for it to be packed once we determine its value. + const uint32_t connect_flag_offset = MQTT_FIXED_HEADER_EXTENDED_SIZE + offset; + offset++; + + if (err_code == NRF_SUCCESS) + { + MQTT_TRC("Encoding Keep Alive Time %04x.", MQTT_KEEPALIVE); + // Pack keep alive time. + err_code = pack_uint16(MQTT_KEEPALIVE, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + } + + if (err_code == NRF_SUCCESS) + { + MQTT_TRC("Encoding Client Id. Str:%s Size:%08x.", + p_client->client_id.p_utf_str, + p_client->client_id.utf_strlen); + + // Pack client id + err_code = pack_utf8_str(&p_client->client_id, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + } + + if (err_code == NRF_SUCCESS) + { + // Pack will topic and QoS + if (p_client->p_will_topic != NULL) + { + MQTT_TRC("Encoding Will Topic. Str:%s Size:%08x.", + p_client->p_will_topic->topic.p_utf_str, + p_client->p_will_topic->topic.utf_strlen); + + // Set Will topic in connect flags. + connect_flags |= MQTT_CONNECT_FLAG_WILL_TOPIC; + + err_code = pack_utf8_str(&p_client->p_will_topic->topic, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + + if (err_code == NRF_SUCCESS) + { + // QoS is always 1 as of now. + connect_flags |= ((p_client->p_will_topic->qos & 0x03) << 3); + connect_flags |= p_client->will_retain << 5; + + if (p_client->p_will_message != NULL) + { + MQTT_TRC("Encoding Will Message. Str:%s Size:%08x.", + p_client->p_will_message->p_utf_str, + p_client->p_will_message->utf_strlen); + + err_code = pack_utf8_str(p_client->p_will_message, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + } + else + { + MQTT_TRC("Encoding Zero Length Will Message."); + err_code = zero_len_str_encode(MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + } + } + } + } + + if (err_code == NRF_SUCCESS) + { + // Pack Username if any. + if (p_client->p_user_name != NULL) + { + connect_flags |= MQTT_CONNECT_FLAG_USERNAME; + + MQTT_TRC("Encoding Username. Str:%s, Size:%08x.", + p_client->p_user_name->p_utf_str, + p_client->p_user_name->utf_strlen); + + err_code = pack_utf8_str(p_client->p_user_name, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + + if (err_code == NRF_SUCCESS) + { + // Pack Password if any. + if (p_client->p_password != NULL) + { + MQTT_TRC("Encoding Password. Str:%s Size:%08x.", + p_client->p_password->p_utf_str, + p_client->p_password->utf_strlen); + + connect_flags |= MQTT_CONNECT_FLAG_PASSWORD; + err_code = pack_utf8_str(p_client->p_password, + MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD, + p_payload, + &offset); + } + } + } + } + + if (err_code == NRF_SUCCESS) + { + // Pack the connect flags. + p_client->p_packet[connect_flag_offset] = connect_flags; + + const uint8_t message_type = MQTT_MESSAGES_OPTIONS(MQTT_PKT_TYPE_CONNECT, + 0, // Duplicate flag not set. + 0, // QoS not set. + 0); // Retain not set. + + offset = mqtt_encode_fixed_header(message_type, + offset, + &p_payload); + + (*p_packet_length) = offset; + (*pp_packet) = p_payload; + } + else + { + (*p_packet_length) = 0; + (*pp_packet) = NULL; + } +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_internal.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_internal.h new file mode 100644 index 0000000..807b2f9 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_internal.h @@ -0,0 +1,446 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file mqtt_internal.h + * + * @brief Function and data structures internal to MQTT module. + */ + +#ifndef MQTT_INTERNAL_H_ +#define MQTT_INTERNAL_H_ + +#include "nordic_common.h" +#include "sdk_common.h" +#include "sdk_config.h" +#include "mqtt.h" +#include "iot_errors.h" +#include "nrf_tls.h" +#include <stdint.h> +#include <string.h> +#include "nrf_error.h" +#include "nrf_tls.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MQTT_MAX_CLIENTS +#define MQTT_MAX_CLIENTS 1 /**< Maximum number of clients that can be managed by the module. */ +#endif //MQTT_MAX_CLIENTS + +#ifndef MQTT_KEEPALIVE +#define MQTT_KEEPALIVE 60 /**< Keep alive time for MQTT (in seconds). Sending of Ping Requests to be keep the connection alive are governed by this value. */ +#endif //MQTT_KEEPALIVE + +#ifndef MQTT_MAX_PACKET_LENGTH +#define MQTT_MAX_PACKET_LENGTH 128 /**< Maximum MQTT packet size that can be sent (including the fixed and variable header). */ +#endif // MQTT_MAX_PACKET_LENGTH + +#define MQTT_FIXED_HEADER_SIZE 2 /**< Fixed header minimum size. Remaining length size is 1 in this case. */ +#define MQTT_FIXED_HEADER_EXTENDED_SIZE 5 /**< Maximum size of the fixed header. Remaining length size is 4 in this case. */ + +/**@brief MQTT Control Packet Types. */ +#define MQTT_PKT_TYPE_CONNECT 0x10 +#define MQTT_PKT_TYPE_CONNACK 0x20 +#define MQTT_PKT_TYPE_PUBLISH 0x30 +#define MQTT_PKT_TYPE_PUBACK 0x40 +#define MQTT_PKT_TYPE_PUBREC 0x50 +#define MQTT_PKT_TYPE_PUBREL 0x60 +#define MQTT_PKT_TYPE_PUBCOMP 0x70 +#define MQTT_PKT_TYPE_SUBSCRIBE 0x82 // QoS 1 for subscribe +#define MQTT_PKT_TYPE_SUBACK 0x90 +#define MQTT_PKT_TYPE_UNSUBSCRIBE 0xA2 +#define MQTT_PKT_TYPE_UNSUBACK 0xB0 +#define MQTT_PKT_TYPE_PINGREQ 0xC0 +#define MQTT_PKT_TYPE_PINGRSP 0xD0 +#define MQTT_PKT_TYPE_DISCONNECT 0xE0 + + +/**@brief Masks for MQTT header flags. */ +#define MQTT_HEADER_DUP_MASK 0x08 +#define MQTT_HEADER_QOS_MASK 0x06 +#define MQTT_HEADER_RETAIN_MASK 0x01 +#define MQTT_HEADER_CONNACK_MASK 0x0F + +/**@brief Masks for MQTT header flags. */ +#define MQTT_CONNECT_FLAG_CLEAN_SESSION 0x02 +#define MQTT_CONNECT_FLAG_WILL_TOPIC 0x04 +#define MQTT_CONNECT_FLAG_WILL_RETAIN 0x20 +#define MQTT_CONNECT_FLAG_PASSWORD 0x40 +#define MQTT_CONNECT_FLAG_USERNAME 0x80 + +/**@brief Size of mandatory header of MQTT Packet. */ +#define MQTT_PKT_HEADER_SIZE 2 + +/**@brief */ +#define MQTT_MAX_PAYLOAD_SIZE 0x0FFFFFFF + +/**@brief Maximum size of variable and payload in the packet. */ +#define MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD (MQTT_MAX_PACKET_LENGTH - MQTT_FIXED_HEADER_EXTENDED_SIZE) + +/**@brief Defines size of uint8 in bytes. */ +#define SIZE_OF_UINT8 1 + +/**@brief Defines size of uint8 in bytes. */ +#define SIZE_OF_UINT16 2 + +/**@brief Computes total size needed to pack a UTF8 string. */ +#define GET_UT8STR_BUFFER_SIZE(STR) (SIZE_OF_UINT16 + (STR)->utf_strlen) + +/**@brief Computes total size needed to pack a binary stream. */ +#define GET_BINSTR_BUFFER_SIZE(STR) ((STR)->bin_strlen) + +/**@brief Unsubscribe packet size. */ +#define MQTT_UNSUBSCRIBE_PKT_SIZE 4 + +/**@brief Sets MQTT Client's state with one indicated in 'STATE'. */ +#define MQTT_SET_STATE(CLIENT, STATE) ((CLIENT)->state |= (STATE)) + +/**@brief Sets MQTT Client's state exclusive to 'STATE'. */ +#define MQTT_SET_STATE_EXCLUSIVE(CLIENT, STATE) ((CLIENT)->state = (STATE)) + +/**@brief Verifies if MQTT Client's state is set with one indicated in 'STATE'. */ +#define MQTT_VERIFY_STATE(CLIENT, STATE) ((CLIENT)->state & (STATE)) + +/**@brief Reset 'STATE' in MQTT Client's state. */ +#define MQTT_RESET_STATE(CLIENT, STATE) ((CLIENT)->state &= ~(STATE)) + +/**@brief Initialize MQTT Client's state. */ +#define MQTT_STATE_INIT(CLIENT) (CLIENT)->state = MQTT_STATE_IDLE + +/**@brief Computes the first byte of MQTT message header based on message type, duplication flag, + * QoS and the retain flag. + */ +#define MQTT_MESSAGES_OPTIONS(TYPE, DUP, QOS, RETAIN) \ + (((TYPE) & 0xF0) | \ + (((DUP) << 3) & 0x08) | \ + (((QOS) << 1) & 0x06) | \ + ((RETAIN) & 0x01)) + + +#define MQTT_EVT_FLAG_NONE 0x00000000 +#define MQTT_EVT_FLAG_INSTANCE_RESET 0x00000001 + + +/** + * @defgroup iot_mqtt_mutex_lock_unlock Module's Mutex Lock/Unlock Macros. + * + * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but + * framework is provided in case the need to use an alternative architecture arises. + * @{ + */ +#define MQTT_MUTEX_LOCK() SDK_MUTEX_LOCK(m_mqtt_mutex) /**< Lock module using mutex */ +#define MQTT_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_mqtt_mutex) /**< Unlock module using mutex */ + +/** @} */ + +/**@brief Check if the input pointer is NULL, if so it returns NRF_ERROR_NULL. + */ +#define NULL_PARAM_CHECK(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return (NRF_ERROR_NULL | IOT_MQTT_ERR_BASE); \ + } + +#define NULL_PARAM_CHECK_VOID(PARAM) \ + if ((PARAM) == NULL) \ + { \ + return; \ + } + +/**@brief MQTT States. */ +typedef enum +{ + MQTT_STATE_IDLE = 0x00000000, /**< Idle state, implying the client entry in the table is unused/free. */ + MQTT_STATE_TCP_CONNECTING = 0x00000001, /**< TCP Connection has been requested, awaiting result of the request. */ + MQTT_STATE_TCP_CONNECTED = 0x00000002, /**< TCP Connection successfully established. */ + MQTT_STATE_CONNECTED = 0x00000004, /**< MQTT Connection successful. */ + MQTT_STATE_PENDING_WRITE = 0x00000008, /**< State that indicates write callback is awaited for an issued request. */ + MQTT_STATE_DISCONNECTING = 0x00000010 /**< TCP Disconnect has been requested, awaiting result of the request. */ +} mqtt_state_t; + + +/**@brief Packs unsigned 8 bit value to the buffer at the offset requested. + * + * @param[in] val Value to be packed. + * @param[in] buffer_len Total size of the buffer on which value is to be packed. + * This shall not be zero. + * @param[out] p_buffer Buffer where the value is to be packed. + * @param[inout] p_offset Offset on the buffer where the value is to be packed. If the procedure + * is successful, the offset is incremented to point to the next write/pack + * location on the buffer. + * + * @retval NRF_SUCCESS if procedure is successful. + * @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length. + */ +uint32_t pack_uint8(uint8_t val, + uint32_t buffer_len, + uint8_t * const p_buffer, + uint32_t * const p_offset); + + +/**@brief Packs unsigned 16 bit value to the buffer at the offset requested. + * + * @param[in] val Value to be packed. + * @param[in] buffer_len Total size of the buffer on which value is to be packed. + * This shall not be zero. + * @param[out] p_buffer Buffer where the value is to be packed. + * @param[inout] p_offset Offset on the buffer where the value is to be packed. If the procedure + * is successful, the offset is incremented to point to the next write/pack + * location on the buffer. + * + * @retval NRF_SUCCESS if the procedure is successful. + * @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length minus + * the size of unsigned 16 bit integer. + */ +uint32_t pack_uint16(uint16_t val, + uint32_t buffer_len, + uint8_t * const p_buffer, + uint32_t * const p_offset); + + +/**@brief Packs utf8 string to the buffer at the offset requested. + * + * @param[in] p_str UTF-8 string and its length to be packed. + * @param[in] buffer_len Total size of the buffer on which string is to be packed. + * This shall not be zero. + * @param[out] p_buffer Buffer where the string is to be packed. + * @param[inout] p_offset Offset on the buffer where the string is to be packed. If the procedure + * is successful, the offset is incremented to point to the next write/pack + * location on the buffer. + * + * @retval NRF_SUCCESS if the procedure is successful. + * @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length minus + * the size of unsigned 16 bit integer. + * @retval NRF_ERROR_NO_MEM if there is no room on the buffer to pack the string. + */ +uint32_t pack_utf8_str(mqtt_utf8_t const * const p_str, + uint32_t buffer_len, + uint8_t * const p_buffer, + uint32_t * const p_offset); + + +/**@brief Packs binary string to the buffer at the offset requested. + * + * @param[in] p_str Binary string and its length to be packed. + * @param[in] buffer_len Total size of the buffer on which string is to be packed. + * @param[in] p_buffer Buffer where the string is to be packed. + * @param[inout] p_offset Offset on the buffer where the string is to be packed. If the procedure + * is successful, the offset is incremented to point to the next write/pack + * location on the buffer. + * + * @retval NRF_SUCCESS if the procedure is successful. + * @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length. + * @retval NRF_ERROR_NO_MEM if there is no room on the buffer to pack the string. + */ +uint32_t pack_bin_str(mqtt_binstr_t const * const p_str, + uint32_t buffer_len, + uint8_t * const p_buffer, + uint32_t * const p_offset); + + +/**@brief Unpacks unsigned 8 bit value from the buffer from the offset requested. + * + * @param[out] p_val Memory where the value is to be unpacked. + * @param[in] buffer_len Total size of the buffer. This shall not be zero. + * @param[in] p_buffer Buffer from which the value is to be unpacked. + * @param[inout] p_offset Offset on the buffer from where the value is to be unpacked. If the + * procedure is successful, the offset is incremented to point to the next + * read/unpack location on the buffer. + * + * @retval NRF_SUCCESS if the procedure is successful. + * @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length. + */ +uint32_t unpack_uint8(uint8_t * p_val, + uint32_t buffer_len, + uint8_t * const p_buffer, + uint32_t * const p_offset); + +/**@brief Unpacks unsigned 16 bit value from the buffer from the offset requested. + * + * @param[out] p_val Memory where the value is to be unpacked. + * @param[in] buffer_len Total size of the buffer. This shall not be zero. + * @param[in] p_buffer Buffer from which the value is to be unpacked. + * @param[inout] p_offset Offset on the buffer from where the value is to be unpacked. If the + * procedure is successful, the offset is incremented to point to the next + * read/unpack location on the buffer. + * + * @retval NRF_SUCCESS if the procedure is successful. + * @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length. + */ +uint32_t unpack_uint16(uint16_t * p_val, + uint32_t buffer_len, + uint8_t * const p_buffer, + uint32_t * const p_offset); + + +/**@brief Unpacks unsigned 16 bit value from the buffer from the offset requested. + * + * @param[out] p_str Memory where the utf8 string and its value are to be unpacked. + * No copy of data is performed for the string. + * @param[in] buffer_len Total size of the buffer. This shall not be zero. + * @param[in] p_buffer Buffer from which the string is to be unpacked. + * @param[inout] p_offset Offset on the buffer from where the value is to be unpacked. If the + * procedure is successful, the offset is incremented to point to the next + * read/unpack location on the buffer. + * + * @retval NRF_SUCCESS if the procedure is successful. + * @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length. + */ +uint32_t unpack_utf8_str(mqtt_utf8_t * const p_str, + uint32_t buffer_len, + uint8_t * const p_buffer, + uint32_t * const p_offset); + + +/**@brief Unpacks binary string from the buffer from the offset requested. + * + * @param[out] p_str Memory where the binary string and its length are to be unpacked. + * No copy of data is performed for the string. + * @param[in] buffer_len Total size of the buffer. This shall not be zero. + * @param[in] p_buffer Buffer where the value is to be unpacked. + * @param[inout] p_offset Offset on the buffer from where the value is to be unpacked. If the + * procedure is successful, the offset is incremented to point to the next + * read/unpack location on the buffer. + * + * @retval NRF_SUCCESS if the procedure is successful. + * @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length. + */ +uint32_t unpack_bin_str(mqtt_binstr_t * const p_str, + uint32_t buffer_len, + uint8_t * const p_buffer, + uint32_t * const p_offset); + + +/**@brief Unpacks utf8 string from the buffer from the offset requested. + * + * @param[out] p_str Memory where the utf8 string and its length are to be unpacked. + * @param[in] buffer_len Total size of the buffer. This shall not be zero. + * @param[in] p_buffer Buffer where the value is to be unpacked. + * @param[inout] p_offset Offset on the buffer from where the value is to be unpacked. If the + * procedure is successful, the offset is incremented to point to the next + * read/unpack location on the buffer. + * + * @retval NRF_SUCCESS if the procedure is successful. + * @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length. + */ +uint32_t zero_len_str_encode(uint32_t buffer_len, + uint8_t * const p_buffer, + uint32_t * const p_offset); + + +/**@brief Computes and encodes length for the MQTT fixed header. + * + * @note The remaining length is not packed as a fixed unsigned 32 bit integer. Instead it is packed + * on algorithm below: + * + * @code + * do + * encodedByte = X MOD 128 + * X = X DIV 128 + * // if there are more data to encode, set the top bit of this byte + * if ( X > 0 ) + * encodedByte = encodedByte OR 128 + * endif + * 'output' encodedByte + * while ( X > 0 ) + * @endcode + * + * @param[in] remaining_length Length of variable header and payload in the MQTT message. + * @param[out] p_buffer Buffer where the length is to be packed. + * @param[inout] p_offset Offset on the buffer where the length is to be packed. + */ +void packet_length_encode(uint32_t remaining_length, uint8_t * p_buffer, uint32_t * p_offset); + + +/**@brief Decode MQTT Packet Length in the MQTT fixed header. + * + * @param[in] p_buffer Buffer where the length is to be decoded. + * @param[in] buffer_len Length of p_buffer + * @param[out] p_remaining_length Length of variable header and payload in the MQTT message. + * @param[inout] p_offset Offset on the buffer from where the length is to be unpacked. + * + * @retval NRF_SUCCESS if the procedure is successful. + * @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length. + */ +uint32_t packet_length_decode(uint8_t * p_buffer, + uint32_t buffer_len, + uint32_t * p_remaining_length, + uint32_t * p_offset); + + +/**@brief Encodes fixed header for the MQTT message and provides pointer to start of the header. + * + * @param[in] message_type Message type containing packet type and the flags. + * Use @ref MQTT_MESSAGES_OPTIONS to construct the message_type. + * @param[in] length Buffer where the message payload along with variable header. + * @param[inout] pp_packet Pointer to the MQTT message variable header and payload. + * The 5 bytes before the start of the message are assumed by the + * routine to be available to pack the fixed header. However, since + * the fixed header length is variable length, the pointer to the + * start of the MQTT message along with encoded fixed header is + * supplied as output parameter if the procedure was successful. + * + * @retval 0xFFFFFFFF if the procedure failed, else length of total MQTT message along with the + * fixed header. + */ +uint32_t mqtt_encode_fixed_header(uint8_t message_type, uint32_t length, uint8_t ** pp_packet); + + +/**@brief Constructs/encodes connect packet. + * + * @param[in] p_client Identifies the client for which the procedure is requested. + * All information required for creating the packet like client id, + * clean session flag, retain session flag etc are assumed to be + * populated for the client instance when this procedure is requested. + * @param[out] pp_packet Pointer to the MQTT connect message. + * @param[out] p_packet_length Length of the connect request. + * + * @retval 0xFFFFFFFF if the procedure failed, else length of total MQTT message along with the + * fixed header. + */ +void connect_request_encode(const mqtt_client_t * p_client, + uint8_t ** pp_packet, + uint32_t * p_packet_length); + +#ifdef __cplusplus +} +#endif + +#endif // MQTT_INTERNAL_H_ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_rx.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_rx.c new file mode 100644 index 0000000..10b0ae4 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_rx.c @@ -0,0 +1,313 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file mqtt_rx.c + * + * @brief Handles packet receive on transport TCP or TLS. + */ +#include "mqtt_internal.h" + +void event_notify(mqtt_client_t * const p_client, const mqtt_evt_t * p_evt, uint32_t flags); +void disconnect_event_notify(mqtt_client_t * p_client, uint32_t result); + +#if MQTT_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME mqtt_rx + +#define NRF_LOG_LEVEL MQTT_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR MQTT_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR MQTT_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define MQTT_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define MQTT_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define MQTT_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define MQTT_ENTRY() MQTT_TRC(">> %s", __func__) +#define MQTT_EXIT() MQTT_TRC("<< %s", __func__) + +#else // MQTT_CONFIG_LOG_ENABLED + +#define MQTT_TRC(...) /**< Disables traces. */ +#define MQTT_DUMP(...) /**< Disables dumping of octet streams. */ +#define MQTT_ERR(...) /**< Disables error logs. */ + +#define MQTT_ENTRY(...) +#define MQTT_EXIT(...) + +#endif // MQTT_CONFIG_LOG_ENABLED + +static uint32_t mqtt_handle_packet(mqtt_client_t * p_client, + uint8_t * p_data, + uint32_t datalen, + uint32_t offset) +{ + mqtt_evt_t evt; + uint32_t err_code = NRF_SUCCESS; + bool notify_event = true; + + // Success by default, overwritten in special cases. + evt.result = NRF_SUCCESS; + + switch (p_data[0] & 0xF0) + { + case MQTT_PKT_TYPE_CONNACK: + { + MQTT_TRC("[%p]: Received MQTT_PKT_TYPE_CONNACK!", p_client); + + if (p_client->protocol_version == MQTT_VERSION_3_1_1) + { + evt.param.connack.session_present_flag = p_data[2] & MQTT_HEADER_CONNACK_MASK; + + MQTT_TRC("[%p]: session_present_flag: %d", + p_client, + evt.param.connack.session_present_flag); + } + + evt.param.connack.return_code = + (mqtt_conn_return_code_t)(p_data[3] & MQTT_HEADER_CONNACK_MASK); + + MQTT_TRC("[%p]: return_code: %d", + p_client, + evt.param.connack.return_code); + + if (evt.param.connack.return_code == MQTT_CONNECTION_ACCEPTED) + { + // Set state. + MQTT_SET_STATE(p_client, MQTT_STATE_CONNECTED); + } + + evt.result = evt.param.connack.return_code; + evt.id = MQTT_EVT_CONNACK; + + break; + } + case MQTT_PKT_TYPE_PUBLISH: + { + evt.param.publish.dup_flag = p_data[0] & MQTT_HEADER_DUP_MASK; + evt.param.publish.retain_flag = p_data[0] & MQTT_HEADER_RETAIN_MASK; + evt.param.publish.message.topic.qos = ((p_data[0] & MQTT_HEADER_QOS_MASK) >> 1); + + MQTT_TRC("[CID %p]: Received MQTT_PKT_TYPE_PUBLISH, QoS:%02x", + p_client, evt.param.publish.message.topic.qos); + + err_code = unpack_utf8_str(&evt.param.publish.message.topic.topic, + datalen, + p_data, + &offset); + + if (err_code == NRF_SUCCESS) + { + if (evt.param.publish.message.topic.qos) + { + err_code = unpack_uint16(&evt.param.publish.message_id, + datalen, + p_data, + &offset); + } + } + + if (err_code == NRF_SUCCESS) + { + err_code = unpack_bin_str(&evt.param.publish.message.payload, + datalen, + p_data, + &offset); + + // Zero length publish messages are permitted. + if (err_code != NRF_SUCCESS) + { + evt.param.publish.message.payload.p_bin_str = NULL; + evt.param.publish.message.payload.bin_strlen = 0; + } + } + + MQTT_TRC("PUB message len %08x, topic len %08x", + evt.param.publish.message.payload.bin_strlen, + evt.param.publish.message.topic.topic.utf_strlen); + + evt.id = MQTT_EVT_PUBLISH; + evt.result = err_code; + + UNUSED_VARIABLE(iot_timer_wall_clock_get(&p_client->last_activity)); + + break; + } + + case MQTT_PKT_TYPE_PUBACK: + { + MQTT_TRC("Received MQTT_PKT_TYPE_PUBACK!"); + + evt.id = MQTT_EVT_PUBACK; + err_code = unpack_uint16(&evt.param.puback.message_id, + datalen, + p_data, + &offset); + evt.result = err_code; + break; + } + + case MQTT_PKT_TYPE_PUBREC: + { + MQTT_TRC("Received MQTT_PKT_TYPE_PUBREC!"); + + evt.id = MQTT_EVT_PUBREC; + err_code = unpack_uint16(&evt.param.pubrec.message_id, + datalen, + p_data, + &offset); + evt.result = err_code; + break; + } + case MQTT_PKT_TYPE_PUBREL: + { + MQTT_TRC("Received MQTT_PKT_TYPE_PUBREL!"); + + evt.id = MQTT_EVT_PUBREL; + err_code = unpack_uint16(&evt.param.pubrel.message_id, + datalen, + p_data, + &offset); + evt.result = err_code; + break; + } + case MQTT_PKT_TYPE_PUBCOMP: + { + MQTT_TRC("Received MQTT_PKT_TYPE_PUBCOMP!"); + + evt.id = MQTT_EVT_PUBCOMP; + err_code = unpack_uint16(&evt.param.pubcomp.message_id, + datalen, + p_data, + &offset); + evt.result = err_code; + break; + } + case MQTT_PKT_TYPE_SUBACK: + { + MQTT_TRC("Received MQTT_PKT_TYPE_SUBACK!"); + + evt.id = MQTT_EVT_SUBACK; + err_code = unpack_uint16(&evt.param.pubrec.message_id, + datalen, + p_data, + &offset); + evt.result = err_code; + break; + } + case MQTT_PKT_TYPE_UNSUBACK: + { + MQTT_TRC("Received MQTT_PKT_TYPE_UNSUBACK!"); + + evt.id = MQTT_EVT_UNSUBACK; + err_code = unpack_uint16(&evt.param.pubrec.message_id, + datalen, + p_data, + &offset); + evt.result = err_code; + break; + } + case MQTT_PKT_TYPE_PINGRSP: + { + MQTT_TRC("Received MQTT_PKT_TYPE_PINGRSP!"); + + // No notification of Ping response to application. + notify_event = false; + break; + } + default: + { + // Nothing to notify. + notify_event = false; + break; + } + } + + if (notify_event == true) + { + event_notify(p_client, &evt, MQTT_EVT_FLAG_NONE); + } + + return err_code; +} + + +uint32_t mqtt_handle_rx_data(mqtt_client_t * p_client, uint8_t * p_data, uint32_t datalen) +{ + uint32_t err_code = NRF_SUCCESS; + uint32_t offset = 0; + + while (offset < datalen) + { + uint32_t start = offset; + uint32_t remaining_length = 0; + + offset = 1; // Skip first byte to offset MQTT packet length. + err_code = packet_length_decode(p_data + start, + datalen - start, + &remaining_length, + &offset); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + uint32_t packet_length = offset + remaining_length; + + if (start + packet_length > datalen) + { + return NRF_ERROR_INVALID_LENGTH; + } + + err_code = mqtt_handle_packet(p_client, + p_data + start, + packet_length, + offset); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + offset = start + packet_length; + } + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_rx.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_rx.h new file mode 100644 index 0000000..7372f0c --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_rx.h @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file mqtt_rx.h + * + * @brief Internal methods to submit received packet. + */ + +#ifndef MQTT_RX_H_ +#define MQTT_RX_H_ + +#include "nordic_common.h" +#include "sdk_common.h" +#include "mqtt.h" +#include "iot_errors.h" +#include "nrf_tls.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Handles MQTT messages received from the peer. For TLS, this routine is evoked to handle + * decrypted application data. For TCP, this routine is evoked to handle TCP data. + * + * @param[in] p_client Identifies the client for which the data was received. + * @param[in] p_data MQTT data received. + * @param[inout] datalen Length of data received. + * + * @retval NRF_SUCCESS if the procedure is successful, else an error code indicating the reason + * for failure. + */ +uint32_t mqtt_handle_rx_data(mqtt_client_t * p_client, uint8_t * p_data, uint32_t datalen); + +#ifdef __cplusplus +} +#endif + +#endif // MQTT_RX_H_ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport.c new file mode 100644 index 0000000..5d0fbd8 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport.c @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file mqtt_transport.c + * + * @brief Internal functions to handle transport in MQTT module. + */ + + +#include "mqtt_transport.h" + + +/**< Function pointer array for TCP/TLS transport handlers. */ +const transport_procedure_t transport_fn[MQTT_TRANSPORT_MAX] = +{ + { + mqtt_client_tcp_connect, + mqtt_client_tcp_write, + mqtt_client_tcp_read, + mqtt_client_tcp_disconnect + }, + { + mqtt_client_tls_connect, + mqtt_client_tls_write, + mqtt_client_tls_read, + mqtt_client_tls_disconnect + } +}; + + +uint32_t mqtt_transport_connect(mqtt_client_t * p_client) +{ + return transport_fn[p_client->transport_type].connect(p_client); +} + + +uint32_t mqtt_transport_write(mqtt_client_t * p_client, uint8_t const * p_data, uint32_t datalen) +{ + return transport_fn[p_client->transport_type].write(p_client, p_data, datalen); +} + + +uint32_t mqtt_transport_read(mqtt_client_t * p_client, uint8_t * p_data, uint32_t datalen) +{ + return transport_fn[p_client->transport_type].read(p_client, p_data, datalen); +} + + +uint32_t mqtt_transport_disconnect(mqtt_client_t * p_client) +{ + return transport_fn[p_client->transport_type].disconnect(p_client); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport.h new file mode 100644 index 0000000..da588a7 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport.h @@ -0,0 +1,233 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file mqtt_transport.h + * + * @brief Internal functions to handle transport in MQTT module. + */ + +#ifndef MQTT_TRANSPORT_H_ +#define MQTT_TRANSPORT_H_ + +#include "mqtt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Transport for handling transport connect procedure. */ +typedef uint32_t (*transport_connect_handler_t)(mqtt_client_t * p_client); + +/**@brief Transport write handler. */ +typedef uint32_t (*transport_write_handler_t)(mqtt_client_t * p_client, uint8_t const * data, uint32_t datalen); + +/**@brief Transport read handler. */ +typedef uint32_t (*transport_read_handler_t)(mqtt_client_t * p_client, uint8_t * data, uint32_t datalen); + +/**@brief Transport disconnect handler. */ +typedef uint32_t (*transport_disconnect_handler_t)(mqtt_client_t * p_client); + +/**@brief Transport procedure handlers. */ +typedef struct +{ + transport_connect_handler_t connect; /**< Transport connect handler. Handles TCP connection callback based on type of transport.*/ + transport_write_handler_t write; /**< Transport write handler. Handles transport write based on type of transport. */ + transport_read_handler_t read; /**< Transport read handler. Handles transport read based on type of transport. */ + transport_disconnect_handler_t disconnect; /**< Transport disconnect handler. Handles transport disconnection based on type of transport. */ +} transport_procedure_t; + + +/**@brief Handles TCP Connection Complete for configured transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_transport_connect(mqtt_client_t * p_client); + + +/**@brief Handles write requests on configured transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * @param[in] p_data Data to be written on the transport. + * @param[in] datalen Length of data to be written on the transport. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_transport_write(mqtt_client_t * p_client, uint8_t const * p_data, uint32_t datalen); + + +/**@brief Handles read requests on configured transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * @param[in] p_data Pointer where read data is to be fetched. + * @param[in] datalen Size of memory provided for the operation. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_transport_read(mqtt_client_t * p_client, uint8_t * p_data, uint32_t datalen); + + +/**@brief Handles transport disconnection requests on configured transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_transport_disconnect(mqtt_client_t * p_client); + + +/**@brief Initiates TCP Connection. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t tcp_request_connection(mqtt_client_t * p_client); + + +/** + * @brief Wait for an incoming MQTT packet. + * The registered callback will be called with the packet payload. + * + * @param[in] p_client Client instance for which the procedure is requested. + * Shall not be NULL. + * @param[in] timeout Maximum interval (in milliseconds) to wait for a packet. + * If timeout is 0, the interval is indefinitely. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t tcp_receive_packet(mqtt_client_t * p_client, uint32_t timeout); + + +/**@brief Handles TCP Connection Complete for TCP(non-secure) transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_client_tcp_connect(mqtt_client_t * p_client); + + +/**@brief Handles write requests on TCP(non-secure) transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * @param[in] p_data Data to be written on the transport. + * @param[in] datalen Length of data to be written on the transport. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_client_tcp_write(mqtt_client_t * p_client, uint8_t const * p_data, uint32_t datalen); + + +/**@brief Handles read requests on TCP(non-secure) transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * @param[in] p_data Pointer where read data is to be fetched. + * @param[in] datalen Size of memory provided for the operation. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_client_tcp_read(mqtt_client_t * p_client, uint8_t * p_data, uint32_t datalen); + + +/**@brief Handles transport disconnection requests on TCP(non-secure) transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_client_tcp_disconnect(mqtt_client_t * p_client); + + +/**@brief Handles read requests on TLS(secure) transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * @param[in] p_data Pointer where read data is to be fetched. + * @param[in] datalen Size of memory provided for the operation. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_client_tls_connect(mqtt_client_t * p_client); + + +/**@brief Handles write requests on TLS(secure) transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * @param[in] p_data Data to be written on the transport. + * @param[in] datalen Length of data to be written on the transport. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_client_tls_write(mqtt_client_t * p_client, uint8_t const * p_data, uint32_t datalen); + + +/**@brief Handles read requests on TLS(secure) transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * @param[in] p_data Pointer where read data is to be fetched. + * @param[in] datalen Size of memory provided for the operation. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_client_tls_read(mqtt_client_t * p_client, uint8_t * p_data, uint32_t datalen); + + +/**@brief Handles transport disconnection requests on TLS(secure) transport. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +uint32_t mqtt_client_tls_disconnect(mqtt_client_t * p_client); + + +/**@brief Aborts TCP connection. + * + * @param[in] p_client Identifies the client on which the procedure is requested. + * + * @retval NRF_SUCCESS or an error code indicating reason for failure. + */ +void mqtt_client_tcp_abort(mqtt_client_t * p_client); + +#ifdef __cplusplus +} +#endif + +#endif // MQTT_TRANSPORT_H_ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_lwip.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_lwip.c new file mode 100644 index 0000000..b609cd3 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_lwip.c @@ -0,0 +1,349 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @brief MQTT Client Implementation over LwIP Stack port on nRF. + * + * This file contains the source code for MQTT Protocol over LwIP Stack for a nRF device. + * The implementation is limited to MQTT Client role only. + */ + + +#include "mqtt_transport.h" +#include "mqtt_internal.h" +#include "mqtt_rx.h" + +#include "lwip/opt.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/pbuf.h" +/*lint -save -e607 */ +#include "lwip/tcp.h" +/*lint -restore -e607 */ + +#if MQTT_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME mqtt_lwip + +#define NRF_LOG_LEVEL MQTT_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR MQTT_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR MQTT_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define MQTT_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define MQTT_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define MQTT_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define MQTT_ENTRY() MQTT_TRC(">> %s", __func__) +#define MQTT_EXIT() MQTT_TRC("<< %s", __func__) + +#else // MQTT_CONFIG_LOG_ENABLED + +#define MQTT_TRC(...) /**< Disables traces. */ +#define MQTT_DUMP(...) /**< Disables dumping of octet streams. */ +#define MQTT_ERR(...) /**< Disables error logs. */ + +#define MQTT_ENTRY(...) +#define MQTT_EXIT(...) + +#endif // MQTT_CONFIG_LOG_ENABLED + +void disconnect_event_notify(mqtt_client_t * p_client, uint32_t result); + + +/**@brief Close TCP connection and clean up client instance. + * + * @param[in] p_client Identifies the client for which the procedure is requested. + */ +static void tcp_close_connection(const mqtt_client_t * p_client) +{ + tcp_arg((struct tcp_pcb *)p_client->tcp_id, NULL); + UNUSED_VARIABLE(tcp_output((struct tcp_pcb *)p_client->tcp_id)); + tcp_recv((struct tcp_pcb *)p_client->tcp_id, NULL); + + UNUSED_VARIABLE(tcp_close((struct tcp_pcb *)p_client->tcp_id)); +} + + +err_t tcp_write_complete_cb(void *p_arg, struct tcp_pcb *ttcp_id, u16_t len) +{ + MQTT_MUTEX_LOCK(); + + mqtt_client_t *p_client = (mqtt_client_t *)(p_arg); + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_DISCONNECTING)) + { + MQTT_TRC("[%p]: Closing TCP connection.", p_client); + tcp_close_connection(p_client); + disconnect_event_notify(p_client, NRF_SUCCESS); + } + else + { + MQTT_RESET_STATE(p_client, MQTT_STATE_PENDING_WRITE); + MQTT_TRC("[%p]: TCP Write Complete.", p_client); + } + + MQTT_MUTEX_UNLOCK(); + + return NRF_SUCCESS; +} + + +uint32_t mqtt_client_tcp_write(mqtt_client_t * p_client, uint8_t const * data, uint32_t datalen) +{ + uint32_t retval = (NRF_ERROR_BUSY | IOT_MQTT_ERR_BASE); + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_PENDING_WRITE)) + { + retval = (NRF_ERROR_BUSY | IOT_MQTT_ERR_BASE); + } + else if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_TCP_CONNECTED)) + { + tcp_sent((struct tcp_pcb *)p_client->tcp_id, tcp_write_complete_cb); + + MQTT_MUTEX_UNLOCK (); + + uint32_t err = tcp_write((struct tcp_pcb *)p_client->tcp_id, + data, + datalen, + TCP_WRITE_FLAG_COPY); + + MQTT_MUTEX_LOCK (); + + if (err == ERR_OK) + { + MQTT_SET_STATE(p_client, MQTT_STATE_PENDING_WRITE); + UNUSED_VARIABLE(iot_timer_wall_clock_get(&p_client->last_activity)); + MQTT_TRC("[%p]: TCP Write in Progress, length 0x%08x.", p_client, datalen); + retval = NRF_SUCCESS; + } + else + { + MQTT_TRC("[%p]: TCP write failed, err = %d", err); + retval = (NRF_ERROR_BUSY | IOT_MQTT_ERR_BASE); + } + } + + return retval; +} + + +uint32_t mqtt_client_tcp_read(mqtt_client_t * p_id, uint8_t * p_data, uint32_t datalen) +{ + return mqtt_handle_rx_data( p_id, p_data, datalen); +} + + +/**@brief Callback registered with TCP to handle incoming data on the connection. */ +err_t recv_callback(void * p_arg, struct tcp_pcb * p_tcp_id, struct pbuf * p_buffer, err_t err) +{ + MQTT_MUTEX_LOCK(); + + mqtt_client_t * p_client = (mqtt_client_t *)(p_arg); + + MQTT_TRC(">> %s, result 0x%08x, buffer %p", __func__, err, p_buffer); + + if (err == ERR_OK && p_buffer != NULL) + { + MQTT_TRC(">> Packet buffer length 0x%08x ", p_buffer->tot_len); + tcp_recved(p_tcp_id, p_buffer->tot_len); + UNUSED_VARIABLE(mqtt_transport_read(p_client, p_buffer->payload, p_buffer->tot_len)); + } + else + { + MQTT_TRC("Error receiving data, closing connection"); + tcp_close_connection(p_client); + disconnect_event_notify(p_client, MQTT_ERR_TRANSPORT_CLOSED); + } + + UNUSED_VARIABLE(pbuf_free(p_buffer)); + + MQTT_MUTEX_UNLOCK(); + + return ERR_OK; +} + + +uint32_t mqtt_client_tcp_connect(mqtt_client_t * p_client) +{ + connect_request_encode(p_client, &p_client->p_pending_packet, &p_client->pending_packetlen); + + // Send MQTT identification message to broker. + uint32_t err = mqtt_client_tcp_write(p_client, p_client->p_pending_packet, + p_client->pending_packetlen); + if (err != ERR_OK) + { + mqtt_client_tcp_abort(p_client); + } + else + { + p_client->p_pending_packet = NULL; + p_client->pending_packetlen = 0; + } + + return err; +} + + +/**@brief TCP Connection Callback. MQTT Connection */ +err_t tcp_connection_callback(void * p_arg, struct tcp_pcb * p_tcp_id, err_t err) +{ + MQTT_MUTEX_LOCK(); + + mqtt_client_t * p_client = (mqtt_client_t *)p_arg; + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_TCP_CONNECTING) && + (err == ERR_OK)) + { + MQTT_SET_STATE(p_client, MQTT_STATE_TCP_CONNECTED); + + // Register callback. + tcp_recv(p_tcp_id, recv_callback); + uint32_t err_code = mqtt_transport_connect(p_client); + + if (err_code != NRF_SUCCESS) + { + MQTT_TRC("Transport connect handler returned %08x", err_code); + disconnect_event_notify(p_client, MQTT_CONNECTION_FAILED); + } + } + + MQTT_MUTEX_UNLOCK(); + + return err; +} + + +void mqtt_client_tcp_abort(mqtt_client_t * p_client) +{ + tcp_abort((struct tcp_pcb *)p_client->tcp_id); + disconnect_event_notify(p_client, MQTT_ERR_TCP_PROC_FAILED); + MQTT_STATE_INIT(p_client); +} + + +void tcp_error_handler(void * p_arg, err_t err) +{ + MQTT_MUTEX_LOCK(); + + mqtt_client_t * p_client = (mqtt_client_t *)(p_arg); + + disconnect_event_notify(p_client, err); + + MQTT_STATE_INIT(p_client); + + MQTT_MUTEX_UNLOCK(); +} + + +err_t tcp_connection_poll(void * p_arg, struct tcp_pcb * p_tcp_id) +{ + MQTT_MUTEX_LOCK(); + + mqtt_client_t * p_client = (mqtt_client_t *)(p_arg); + + p_client->poll_abort_counter++; + + MQTT_MUTEX_UNLOCK(); + + return ERR_OK; +} + + +uint32_t tcp_request_connection(mqtt_client_t * p_client) +{ + p_client->poll_abort_counter = 0; + p_client->tcp_id = (uint32_t)tcp_new_ip6(); + + err_t err = tcp_connect((struct tcp_pcb *)p_client->tcp_id, + (ip_addr_t *)&p_client->broker_addr, + p_client->broker_port, + tcp_connection_callback); + + if (err != ERR_OK) + { + UNUSED_VARIABLE(mqtt_abort(p_client)); + } + else + { + tcp_arg((struct tcp_pcb *)p_client->tcp_id, p_client); + tcp_err((struct tcp_pcb *)p_client->tcp_id, tcp_error_handler); + tcp_poll((struct tcp_pcb *)p_client->tcp_id, tcp_connection_poll, 10); + tcp_accept((struct tcp_pcb *)p_client->tcp_id, tcp_connection_callback); + + MQTT_SET_STATE(p_client, MQTT_STATE_TCP_CONNECTING); + } + + return err; +} + + +uint32_t mqtt_client_tcp_disconnect(mqtt_client_t * p_client) +{ + uint32_t err_code = NRF_ERROR_INVALID_STATE; + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_CONNECTED)) + { + const uint8_t packet[] = {MQTT_PKT_TYPE_DISCONNECT, 0x00}; + UNUSED_VARIABLE(tcp_write((struct tcp_pcb *)p_client->tcp_id, + (void *)packet, + sizeof(packet), + 1)); + + tcp_close_connection(p_client); + err_code = NRF_SUCCESS; + } + else if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_TCP_CONNECTED)) + { + tcp_close_connection(p_client); + err_code = NRF_SUCCESS; + } + + return err_code; +} + + +uint32_t tcp_receive_packet(mqtt_client_t * p_client, uint32_t timeout) +{ + // This is not used in the lwip transport implementation. + return NRF_ERROR_NOT_SUPPORTED | IOT_MQTT_ERR_BASE; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_socket.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_socket.c new file mode 100644 index 0000000..27bf6b6 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_socket.c @@ -0,0 +1,319 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @brief MQTT Client Implementation over BSD Socket API on nRF. + * + * This file contains the source code for MQTT Protocol over BSD Socket API for a nRF device. + * The implementation is limited to MQTT Client role only. + */ + + +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include "mem_manager.h" +#include "mqtt_transport.h" +#include "mqtt_internal.h" +#include "mqtt_rx.h" + +#if MQTT_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME mqtt_soc + +#define NRF_LOG_LEVEL MQTT_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR MQTT_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR MQTT_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define MQTT_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define MQTT_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define MQTT_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define MQTT_ENTRY() MQTT_TRC(">> %s", __func__) +#define MQTT_EXIT() MQTT_TRC("<< %s", __func__) + +#else // MQTT_CONFIG_LOG_ENABLED + +#define MQTT_TRC(...) /**< Disables traces. */ +#define MQTT_DUMP(...) /**< Disables dumping of octet streams. */ +#define MQTT_ERR(...) /**< Disables error logs. */ + +#define MQTT_ENTRY(...) +#define MQTT_EXIT(...) + +#endif // MQTT_CONFIG_LOG_ENABLED + +void disconnect_event_notify(mqtt_client_t * p_client, uint32_t result); + + +/**@brief Close TCP connection and clean up client instance. + * + * @param[in] p_client Identifies the client for which the procedure is requested. + */ +static void tcp_close_connection(const mqtt_client_t * p_client) +{ + MQTT_TRC("Closing socket %d", p_client->socket_fd); + UNUSED_VARIABLE(close(p_client->socket_fd)); +} + + +uint32_t mqtt_client_tcp_write(mqtt_client_t * p_client, uint8_t const * data, uint32_t datalen) +{ + uint32_t err_code = (NRF_ERROR_BUSY | IOT_MQTT_ERR_BASE); + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_PENDING_WRITE)) + { + err_code = (NRF_ERROR_BUSY | IOT_MQTT_ERR_BASE); + } + else if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_TCP_CONNECTED)) + { + MQTT_TRC("[%p]: TCP writing %d bytes.", p_client, datalen); + MQTT_SET_STATE(p_client, MQTT_STATE_PENDING_WRITE); + + MQTT_MUTEX_UNLOCK(); + + ssize_t nbytes = send(p_client->socket_fd, data, datalen, 0); + + MQTT_MUTEX_LOCK(); + + MQTT_RESET_STATE(p_client, MQTT_STATE_PENDING_WRITE); + + if (nbytes == datalen) + { + MQTT_TRC("[%p]: TCP write complete.", p_client); + UNUSED_VARIABLE(iot_timer_wall_clock_get(&p_client->last_activity)); + err_code = NRF_SUCCESS; + } + else + { + MQTT_TRC("TCP write failed, errno = %d, closing connection", errno); + tcp_close_connection(p_client); + disconnect_event_notify(p_client, MQTT_ERR_TRANSPORT_CLOSED); + err_code = (NRF_ERROR_INTERNAL | IOT_MQTT_ERR_BASE); + } + } + else + { + err_code = MQTT_ERR_NOT_CONNECTED; + } + + return err_code; +} + + +uint32_t mqtt_client_tcp_read(mqtt_client_t * p_client, uint8_t * p_data, uint32_t datalen) +{ + return mqtt_handle_rx_data(p_client, p_data, datalen); +} + + +uint32_t mqtt_client_tcp_connect(mqtt_client_t * p_client) +{ + uint32_t err_code; + + connect_request_encode(p_client, &p_client->p_pending_packet, &p_client->pending_packetlen); + + // Send MQTT identification message to broker. + MQTT_SET_STATE(p_client, MQTT_STATE_PENDING_WRITE); + + MQTT_MUTEX_UNLOCK(); + + ssize_t nbytes = send(p_client->socket_fd, + p_client->p_pending_packet, + p_client->pending_packetlen, + 0); + + MQTT_MUTEX_LOCK(); + + MQTT_RESET_STATE(p_client, MQTT_STATE_PENDING_WRITE); + + if (nbytes == p_client->pending_packetlen) + { + UNUSED_VARIABLE(iot_timer_wall_clock_get(&p_client->last_activity)); + p_client->p_pending_packet = NULL; + p_client->pending_packetlen = 0; + err_code = NRF_SUCCESS; + } + else + { + mqtt_client_tcp_abort(p_client); + err_code = (NRF_ERROR_INTERNAL | IOT_MQTT_ERR_BASE); + } + + return err_code; +} + + +void mqtt_client_tcp_abort(mqtt_client_t * p_client) +{ + tcp_close_connection(p_client); + disconnect_event_notify(p_client, MQTT_ERR_TCP_PROC_FAILED); +} + + +uint32_t tcp_receive_packet(mqtt_client_t * p_client, uint32_t timeout) +{ + if (timeout != 0) + { + // TODO: Implement support for timeout. + return NRF_ERROR_NOT_SUPPORTED | IOT_MQTT_ERR_BASE; + } + + uint8_t * p_packet = nrf_malloc(MQTT_MAX_PACKET_LENGTH); + if (p_packet == NULL) + { + return NRF_ERROR_NO_MEM | IOT_MQTT_ERR_BASE; + } + + MQTT_MUTEX_UNLOCK(); + + ssize_t p_len = recv(p_client->socket_fd, p_packet, MQTT_MAX_PACKET_LENGTH, 0); + + MQTT_MUTEX_LOCK(); + + uint32_t err_code; + + if (p_len > 0) + { + err_code = mqtt_transport_read(p_client, p_packet, p_len); + MQTT_TRC("Received %d bytes from %d: 0x%08x", + p_len, p_client->socket_fd, err_code); + } + else if (p_len == 0) + { + // Receiving 0 bytes indicates an orderly shutdown. + MQTT_TRC("Received end of stream, closing connection"); + tcp_close_connection(p_client); + disconnect_event_notify(p_client, MQTT_ERR_TRANSPORT_CLOSED); + err_code = NRF_SUCCESS; + } + else + { + MQTT_TRC("Error receiving data, errno = %d, closing connection", errno); + mqtt_client_tcp_abort(p_client); + err_code = (NRF_ERROR_INVALID_DATA | IOT_MQTT_ERR_BASE); + } + + nrf_free(p_packet); + + return err_code; +} + + +uint32_t tcp_request_connection(mqtt_client_t * p_client) +{ + uint32_t err_code = NRF_SUCCESS; + + p_client->socket_fd = socket(AF_INET6, SOCK_STREAM, 0); + MQTT_TRC("Created socket %d", p_client->socket_fd); + if (p_client->socket_fd < 0) + { + err_code = (NRF_ERROR_INTERNAL | IOT_MQTT_ERR_BASE); + } + + if (err_code == NRF_SUCCESS) + { + struct sockaddr_in6 dest; + memset(&dest, 0, sizeof(dest)); + dest.sin6_family = AF_INET6; + dest.sin6_port = htons(p_client->broker_port); + memcpy(&dest.sin6_addr, p_client->broker_addr.u8, sizeof(dest.sin6_addr)); + + int ret = connect(p_client->socket_fd, (struct sockaddr *)&dest, sizeof(dest)); + if (ret == 0) + { + MQTT_SET_STATE(p_client, MQTT_STATE_TCP_CONNECTED); + err_code = mqtt_transport_connect(p_client); + MQTT_TRC("Sent connect %d: 0x%08x", p_client->socket_fd, err_code); + } + else + { + mqtt_client_tcp_abort(p_client); + err_code = (NRF_ERROR_INTERNAL | IOT_MQTT_ERR_BASE); + } + } + + while (!MQTT_VERIFY_STATE(p_client, MQTT_STATE_CONNECTED) && err_code == NRF_SUCCESS) + { + // Receive until connected. + MQTT_TRC("Receive until connected"); + err_code = tcp_receive_packet(p_client, 0); + } + + MQTT_TRC("Connect completed"); + return err_code; +} + + +uint32_t mqtt_client_tcp_disconnect(mqtt_client_t * p_client) +{ + uint32_t err_code; + + if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_CONNECTED)) + { + const uint8_t packet[] = {MQTT_PKT_TYPE_DISCONNECT, 0x00}; + MQTT_SET_STATE(p_client, MQTT_STATE_PENDING_WRITE); + + MQTT_MUTEX_UNLOCK(); + + UNUSED_VARIABLE(send(p_client->socket_fd, (void *)packet, sizeof(packet), 0)); + + MQTT_MUTEX_LOCK(); + + MQTT_RESET_STATE(p_client, MQTT_STATE_PENDING_WRITE); + tcp_close_connection(p_client); + err_code = NRF_SUCCESS; + } + else if (MQTT_VERIFY_STATE(p_client, MQTT_STATE_TCP_CONNECTED)) + { + tcp_close_connection(p_client); + err_code = NRF_SUCCESS; + } + else + { + err_code = (NRF_ERROR_INVALID_STATE | IOT_MQTT_ERR_BASE); + } + + return err_code; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_tls.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_tls.c new file mode 100644 index 0000000..8f74600 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_tls.c @@ -0,0 +1,185 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @brief MQTT Client Implementation TLS layer. + * + * This file contains the source code for MQTT Protocol TLS layer for a nRF device. + * The implementation is limited to MQTT Client role only. + */ + + +#include "mqtt_transport.h" +#include "mqtt_internal.h" +#include "mqtt_rx.h" +#include "mem_manager.h" + +#if MQTT_CONFIG_LOG_ENABLED + +#define NRF_LOG_MODULE_NAME mqtt_tls + +#define NRF_LOG_LEVEL MQTT_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR MQTT_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR MQTT_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define MQTT_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define MQTT_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define MQTT_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#define MQTT_ENTRY() MQTT_TRC(">> %s", __func__) +#define MQTT_EXIT() MQTT_TRC("<< %s", __func__) + +#else // MQTT_CONFIG_LOG_ENABLED + +#define MQTT_TRC(...) /**< Disables traces. */ +#define MQTT_DUMP(...) /**< Disables dumping of octet streams. */ +#define MQTT_ERR(...) /**< Disables error logs. */ + +#define MQTT_ENTRY(...) +#define MQTT_EXIT(...) + +#endif // MQTT_CONFIG_LOG_ENABLED + +uint32_t mqtt_client_tls_output_handler(nrf_tls_instance_t const * p_instance, + uint8_t const * p_data, + uint32_t datalen) +{ + NULL_PARAM_CHECK(p_instance); + + uint32_t err_code = NRF_ERROR_INTERNAL; + mqtt_client_t * p_client = (mqtt_client_t *)p_instance->transport_id; + + MQTT_MUTEX_LOCK(); + + MQTT_TRC(">> %s, client %p", __func__, p_client); + + if (p_client != NULL) + { + err_code = mqtt_client_tcp_write(p_client, p_data, datalen); + } + + MQTT_TRC("<< %s, client %p, result 0x%08x", __func__, + p_client, err_code); + + MQTT_MUTEX_UNLOCK(); + + return err_code; +} + + +uint32_t mqtt_client_tls_connect(mqtt_client_t * p_client) +{ + const nrf_tls_options_t tls_option = + { + .output_fn = mqtt_client_tls_output_handler, + .transport_type = NRF_TLS_TYPE_STREAM, + .role = NRF_TLS_ROLE_CLIENT, + .p_key_settings = p_client->p_security_settings + }; + + connect_request_encode(p_client, + &p_client->p_pending_packet, + &p_client->pending_packetlen); + + p_client->tls_instance.transport_id = (uint32_t)p_client; + + MQTT_MUTEX_UNLOCK (); + + uint32_t err_code = nrf_tls_alloc(&p_client->tls_instance, &tls_option); + + MQTT_MUTEX_LOCK (); + + return err_code; +} + + +uint32_t mqtt_client_tls_write(mqtt_client_t * p_client, + uint8_t const * p_data, + uint32_t datalen) +{ + MQTT_MUTEX_UNLOCK (); + + uint32_t err_code = nrf_tls_write(&p_client->tls_instance, p_data, &datalen); + + MQTT_MUTEX_LOCK (); + + return err_code; +} + + +uint32_t mqtt_client_tls_read(mqtt_client_t * p_client, uint8_t * p_data, uint32_t datalen) +{ + uint32_t err = nrf_tls_input(&p_client->tls_instance, p_data, datalen); + + if ((err == NRF_SUCCESS) && (p_client->p_pending_packet == NULL)) + { + uint32_t rx_datalen = 1024; + uint8_t * p_mqtt_data = nrf_malloc(1024); + + if (p_data != NULL) + { + MQTT_MUTEX_UNLOCK (); + + err = nrf_tls_read(&p_client->tls_instance, + p_mqtt_data, + &rx_datalen); + + MQTT_MUTEX_LOCK (); + + if ((err == NRF_SUCCESS) && (rx_datalen > 0)) + { + err = mqtt_handle_rx_data(p_client, p_mqtt_data, rx_datalen); + } + + nrf_free(p_mqtt_data); + } + } + + return err; +} + + +uint32_t mqtt_client_tls_disconnect(mqtt_client_t * p_client) +{ + return mqtt_client_tcp_disconnect(p_client); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/README.md b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/README.md new file mode 100644 index 0000000..9ec0b8d --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/README.md @@ -0,0 +1,28 @@ +# Socket module + +The socket module contains the BSD socket implementation for the IoT SDK. The purpose of the socket +API is to + +* Provide a common API for all platforms +* Simplify porting of pc network applications + +The socket API hides details of the underlying transport, but supports proprietary extensions for +controlling configuration settings and using underlying transport layers. + +<pre> +socket/ + api/ - Public socket API headers + common/ - Common implementation of API and implementation code shared by all platforms (main socket API implementation, with hooks for different transports) + libraries/ - Generic libraries that are not tied to a specific platform + portdb/ - Port database to track and allocate socket ports + addr_util/ - Common address utilities + mbuf/ - Memory buffer utilities + transport/ - Transport/network stack hooks + ipv6/ - Nordic IPv6 stack transport hook + lwip/ - LwIP transport hook + test/ - Integration tests shared between transport stacks + platform/ - Platform specific code + ble/ - BLE specific code (only wrappers around sd_ble_app_evt_()) + config/ - Configuration socket implementations + medium/ - Medium configuration socket implementation +</pre> diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/arpa/inet.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/arpa/inet.h new file mode 100644 index 0000000..c1749d3 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/arpa/inet.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef SOCKET_ARPA_INET_H__ +#define SOCKET_ARPA_INET_H__ + +#include "socket_api.h" + +#endif diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/netinet/in.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/netinet/in.h new file mode 100644 index 0000000..bfcf881 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/netinet/in.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef SOCKET_NETINET_IN_H__ +#define SOCKET_NETINET_IN_H__ + +#include "socket_api.h" + +#endif diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/socket_api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/socket_api.h new file mode 100644 index 0000000..37e50d4 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/socket_api.h @@ -0,0 +1,593 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file socket_api.h + * + * @defgroup iot_socket BSD Socket interface + * @ingroup iot_sdk_socket + * @{ + * @brief Nordic socket interface for IoT. + * + * @details This module provides the socket interface for writing IoT applications. The API is + * designed to be compatible with the POSIX/BSD socket interface for the purpose of + * making porting easy. The socket options API has been extended to support configuring + * Nordic BLE stack, tuning of RF parameters as well as security options. + */ +#ifndef SOCKET_API_H__ +#define SOCKET_API_H__ + +#include <stdint.h> + +#include "sdk_common.h" +#include "iot_defines.h" +#include "errno.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__GNUC__) || (__GNUC__ == 0) +typedef int32_t ssize_t; +#else +#include <sys/types.h> +#ifdef __SES_ARM +typedef int32_t ssize_t; +#endif +#endif + +#ifndef htons +#define htons(x) HTONS(x) /**< Convert byte order from host to network (short). */ +#endif + +#ifndef htonl +#define htonl(x) HTONL(x) /**< Convert byte order from host to network (long). */ +#endif + +#ifndef ntohs +#define ntohs(x) NTOHS(x) /**< Convert byte order from network to host (short). */ +#endif + +#ifndef ntohl +#define ntohl(x) NTOHL(x) /**< Convert byte order from network to host (long). */ +#endif + +/**@defgroup socket_families Values for socket_family_t + * @ingroup iot_socket + * @{ + */ +#define AF_INET 2 /**< IPv4 socket family. */ +#define AF_INET6 10 /**< IPv6 socket family. */ +#if defined(NRF52) || defined(NRF52_SERIES) +#define AF_NRF_CFG 39 /**< nRF configuration socket.*/ +#endif +/**@} */ + +/**@defgroup socket_types Values for socket_type_t + * @ingroup iot_socket + * @{ + */ +#define SOCK_STREAM 1 /**< TCP socket type. */ +#define SOCK_DGRAM 2 /**< UDP socket type. */ +/**@} */ + +/**@defgroup socket_protocols Values for socket_protocol_t + * @ingroup iot_socket + * @{ + */ +#define IPPROTO_TCP 1 /**< Use TCP as transport protocol. */ +#define IPPROTO_UDP 2 /**< Use UDP as transport protocol. */ +/**@} */ + +/**@defgroup socket_send_recv_flags Socket send/recv flags + * @ingroup iot_socket + * @{ + */ +#define MSG_DONTROUTE 0x01 /**< Send only to hosts on directly connected networks. */ +#define MSG_DONTWAIT 0x02 /**< Enables non-blocking operation. */ +#define MSG_OOB 0x04 /**< Sends out-of-band data on sockets that support this. */ +#define MSG_PEEK 0x08 /**< Return data from the beginning of receive queue without removing data from the queue. */ +#define MSG_WAITALL 0x10 /**< Request a blocking operation until the request is satisfied. */ +/**@} */ + +#if defined(NRF52) || defined(NRF52_SERIES) +/** + * @defgroup socket_option_levels Values for socket_opt_lvl_t + * @ingroup iot_socket + * @{ + */ +#define SOL_SOCKET 1 /**< Standard socket options. */ +#define SOL_NRF_MEDIUM 2 /**< Nordic medium socket options. Use this to control medium parameters. */ +/**@} */ + +/**@defgroup socket_medium_options Medium socket option level types + * @ingroup iot_socket + * @{ + */ +#define MEDIUM_INIT_PARAMS 1 /**< Medium initialization parameters. */ +/**@} + */ +#endif + +/**@defgroup fcnt_commands fcntl commands + * @ingroup iot_socket + * @{ + */ +#define F_SETFL 1 /**< Set flag. */ +#define F_GETFL 2 /**< Get flag. */ +/**@} */ + +/**@defgroup fcnt_flags fcntl flags + * @ingroup iot_socket + * @{ + */ +#define O_NONBLOCK 0x01 /**< Use non-blocking I/O. */ +/**@} */ + +/** + * @brief Socket module size type. + */ +typedef uint32_t socklen_t; + +/** + * @brief Socket port type. + */ +typedef uint16_t in_port_t; + +/** + * @brief Structure specifying time interval. + */ +struct timeval +{ + uint32_t tv_sec; /**< Time interval seconds. */ + uint32_t tv_usec; /**< Time interval microseconds. */ +}; + +/** + * @brief Socket families. + * + * @details For a list of valid values, refer to @ref socket_families. + */ +typedef int socket_family_t; +typedef socket_family_t sa_family_t; + +/** + * @brief Socket types. + * + * @details For a list of valid values refer to @ref socket_types. + */ +typedef int socket_type_t; + +/** + * @brief Socket protocols. + * + * @details Use 0 if you do not want do specify socket protocol, which should be sufficient for most users. + * Other values are only provided for socket API compatibility, see @ref socket_protocols. + */ +typedef int socket_protocol_t; + +/** + * @if (IOT) + * @brief Socket option levels. + * + * @details For a list of valid values, refer to @ref socket_option_levels. + * @endif + */ +typedef int socket_opt_lvl_t; + +/** + * @brief Generic socket address. + * + * @details Only provided for API compatibility. + */ +struct sockaddr +{ + uint8_t sa_len; + socket_family_t sa_family; + char sa_data[]; +}; + +/** + * @brief IPv6 address. + */ +struct in6_addr +{ + uint8_t s6_addr[16]; +}; + +/** + * @brief IPv4 address. + */ +typedef uint32_t in_addr_t; + +/** + * @brief IPv4 address structure. + */ +struct in_addr +{ + in_addr_t s_addr; +}; + +/** + * @brief Global IPv6 any-address. + */ +extern const struct in6_addr in6addr_any; + +/** + * @brief Global IPv4 any-address. + */ +extern const struct in_addr inaddr_any; + +/** + * @brief Address record for IPv6 addresses. + * + * @details Contains the address and port of the host, as well as other socket options. All fields + * in this structure are compatible with the POSIX variant for API compatibility. + */ +struct sockaddr_in6 +{ + uint8_t sin6_len; /**< Length of this data structure. */ + sa_family_t sin6_family; /**< Socket family. */ + in_port_t sin6_port; /**< Port, in network byte order. */ + + uint32_t sin6_flowinfo; /**< IPv6 flow info parameters. Not used. */ + struct in6_addr sin6_addr; /**< IPv6 address. */ + uint32_t sin6_scope_id; /**< IPv6 scope ID. Not used. */ +}; + +/** + * @brief Address record for IPv4 addresses. + * + * @details Contains the address and port of the host. All fields + * in this structure are compatible with the POSIX variant for API compatibility. + */ +struct sockaddr_in +{ + uint8_t sin_len; /**< Length of this data structure. */ + sa_family_t sin_family; /**< Socket family. */ + in_port_t sin_port; /**< Port, in network byte order. */ + + struct in_addr sin_addr; /**< IPv4 address. */ +}; + +typedef struct sockaddr sockaddr_t; +typedef struct sockaddr_in6 sockaddr_in6_t; +typedef struct in6_addr in6_addr_t; +typedef struct sockaddr_in sockaddr_in_t; + +/** + * @brief Function for creating a socket. + * + * @details API to create a socket that can be used for network communication independently + * of lower protocol layers. + * + * @param[in] family The protocol family of the network protocol to use. Currently, only + * AF_INET6 is supported. + * @param[in] type The protocol type to use for this socket. + * @param[in] protocol The transport protocol to use for this socket. + * + * @return A non-negative socket descriptor on success, or -1 on error. + */ +int socket(socket_family_t family, socket_type_t type, socket_protocol_t protocol); + +/** + * @brief Function for closing a socket and freeing any resources held by it. + * + * @details If the socket is already closed, this function is a noop. + * + * @param[in] sock The socket to close. + * + * @return 0 on success, or -1 on error. + */ +int close(int sock); + +/** + * @brief Function for controlling file descriptor options. + * + * @details Set or get file descriptor options or flags. For a list of supported commands, refer to @ref fcnt_commands. + * For a list of supported flags, refer to @ref fcnt_flags. + * + * @param[in] fd The descriptor to set options on. + * @param[in] cmd The command class for options. + * @param[in] flags The flags to set. + */ +int fcntl(int fd, int cmd, int flags); + +/** + * @brief Function for connecting to an endpoint with a given address. + * + * @details The socket handle must be a valid handle that has not yet been connected. Running + * connect on a connected handle will return an error. + * + * @param[in] sock The socket to use for connection. + * @param[in] p_servaddr The address of the server to connect to. Currently, sockaddr_in6 is + * the only supported type. + * @param[in] addrlen The size of the p_servaddr argument. + * + * @return 0 on success, or -1 on error. + */ +int connect(int sock, const void * p_servaddr, socklen_t addrlen); + +/** + * @brief Function for sending data through a socket. + * + * @details By default, this function will block unless the O_NONBLOCK + * socket option has been set, OR MSG_DONTWAIT is passed as a flag. In that case, the + * method will return immediately. + * + * @param[in] sock The socket to write data to. + * @param[in] p_buff Buffer containing the data to send. + * @param[in] nbytes Size of data contained on p_buff. + * @param[in] flags Flags to control send behavior. + * + * @return The number of bytes that were sent on success, or -1 on error. + */ +ssize_t send(int sock, const void * p_buff, size_t nbytes, int flags); + +/** + * @brief Function for sending datagram through a socket. + * + * @details By default, this function will block if the lower layers are not able to process the + * packet, unless the O_NONBLOCK socket option has been set, OR MSG_DONTWAIT is passed as a flag. + * In that case, the method will return immediately. + * + * @param[in] sock The socket to write data to. + * @param[in] p_buff Buffer containing the data to send. + * @param[in] nbytes Size of data contained in p_buff. + * @param[in] flags Flags to control send behavior. + * @param[in] p_servaddr The address of the server to send to. Currently, sockaddr_in6 is + * the only supported type. + * @param[in] addrlen The size of the p_servaddr argument. + * + * @return The number of bytes that were sent on success, or -1 on error. + */ +ssize_t sendto(int sock, + const void * p_buff, + size_t nbytes, + int flags, + const void * p_servaddr, + socklen_t addrlen); + +/** + * @brief Function for writing data to a socket. See \ref send() for details. + * + * @param[in] sock The socket to write data to. + * @param[in] p_buff Buffer containing the data to send. + * @param[in] nbytes Size of data contained in p_buff. + * + * @return The number of bytes that were sent on success, or -1 on error. + */ +ssize_t write(int sock, const void * p_buff, size_t nbytes); + +/** + * @brief Function for receiving data on a socket. + * + * @details API for receiving data from a socket. By default, this function will block, unless the + * O_NONBLOCK socket option has been set, or MSG_DONTWAIT is passed as a flag. + * + * @param[in] sock The socket to receive data from. + * @param[out] p_buff Buffer to hold the data to be read. + * @param[in] nbytes Number of bytes to read. Should not be larger than the size of p_buff. + * @param[in] flags Flags to control receive behavior. + * + * @return The number of bytes that were read, or -1 on error. + */ +ssize_t recv(int sock, void * p_buff, size_t nbytes, int flags); + +/** + * @brief Function for receiving datagram on a socket. + * + * @details API for receiving data from a socket. By default, this function will block, unless the + * O_NONBLOCK socket option has been set, or MSG_DONTWAIT is passed as a flag. + * + * @param[in] sock The socket to receive data from. + * @param[out] p_buff Buffer to hold the data to be read. + * @param[in] nbytes Number of bytes to read. Should not be larger than the size of p_buff. + * @param[in] flags Flags to control receive behavior. + * @param[out] p_cliaddr Socket address that will be set to the client's address. + * @param[inout] p_addrlen The size of the p_cliaddr passed. Might be modified by the function. + * + * @return The number of bytes that were read, or -1 on error. + */ +ssize_t recvfrom(int sock, + void * p_buff, + size_t nbytes, + int flags, + void * p_cliaddr, + socklen_t * p_addrlen); + +/** + * @brief Function for reading data from a socket. See \ref recv() for details. + * + * @param[in] sock The socket to receive data from. + * @param[out] p_buff Buffer to hold the data to be read. + * @param[in] nbytes Number of bytes to read. Should not be larger than the size of p_buff. + * + * @return The number of bytes that were read, or -1 on error. + */ +ssize_t read(int sock, void * p_buff, size_t nbytes); + +/** + * @defgroup fd_set_api API for file descriptor set + * @ingroup iot_socket + * @details File descriptor sets are used as input to the select() function for doing I/O + * multiplexing. The maximum number of descriptors contained in a set is defined by + * FD_SETSIZE. + * + * @{ + */ +#ifndef FD_ZERO + +typedef uint32_t fd_set; +#define FD_ZERO(set) (*(set) = 0) /**< Clear the entire set. */ +#define FD_SET(fd, set) (*(set) |= (1u << (fd))) /**< Set a bit in the set. */ +#define FD_CLR(fd, set) (*(set) &= ~(1u << (fd))) /**< Clear a bit in the set. */ +#define FD_ISSET(fd, set) (*(set) & (1u << (fd))) /**< Check if a bit in the set is set. */ +#define FD_SETSIZE sizeof(fd_set) /**< The max size of a set. */ + +#endif +/**@} */ + +/** + * @brief Function for waiting for read, write, or exception events on a socket. + * + * @details Wait for a set of socket descriptors to be ready for reading, writing, or having + * exceptions. The set of socket descriptors is configured before calling this function. + * This function will block until any of the descriptors in the set has any of the required + * events. This function is mostly useful when using O_NONBLOCK or MSG_DONTWAIT options + * to enable async operation. + * + * @param[in] nfds The highest socket descriptor value contained in the sets. + * @param[inout] p_readset The set of descriptors for which to wait for read events. Set to NULL + * if not used. + * @param[inout] p_writeset The set of descriptors for which to wait for write events. Set to NULL + * if not used. + * @param[inout] p_exceptset The set of descriptors for which to wait for exception events. Set to + * NULL if not used. + * @param[in] p_timeout The timeout to use for select call. Set to NULL if waiting forever. + * + * @return The number of ready descriptors contained in the descriptor sets on success, or -1 on error. + */ +int select(int nfds, + fd_set * p_readset, + fd_set * p_writeset, + fd_set * p_exceptset, + const struct timeval * p_timeout); + +/** + * @brief Function for setting socket options for a given socket. + * + * @details The options are grouped by level, and the option value should be the expected for the + * given option, and the lifetime must be longer than that of the socket. + * + * @param[in] sock The socket for which to set the option. + * @param[in] level The level or group to which the option belongs. + * @param[in] optname The name of the socket option. + * @param[in] p_optval The value to be stored for this option. + * @param[in] optlen The size of p_optval. + * + * @return 0 on success, or -1 on error. + */ +int setsockopt(int sock, + socket_opt_lvl_t level, + int optname, + const void * p_optval, + socklen_t optlen); + +/** + * @brief Function for getting socket options for a given socket. + * + * @details The options are grouped by level, and the option value is the value described by the + * option name. + * + * @param[in] sock The socket for which to set the option. + * @param[in] level The level or group to which the option belongs. + * @param[in] optname The name of the socket option. + * @param[out] p_optval Pointer to the storage for the option value. + * @param[inout] p_optlen The size of p_optval. Can be modified to the actual size of p_optval. + * + * @return 0 on success, or -1 on error. + */ +int getsockopt(int sock, + socket_opt_lvl_t level, + int optname, + void * p_optval, + socklen_t * p_optlen); + +/** + * @brief Function for binding a socket to an address and port. + * + * @details The provided address must be supported by the socket protocol family. + * + * @param[in] sock The socket descriptor to bind. + * @param[in] p_myaddr The address to bind this socket to. + * @param[in] addrlen The size of p_myaddr. + * + * @return 0 on success, or -1 on error. + */ +int bind(int sock, const void * p_myaddr, socklen_t addrlen); + +/** + * @brief Function for marking a socket as listenable. + * + * @details Once a socket is marked as listenable, it cannot be unmarked. It is important to + * consider the backlog parameter, as it will affect how much memory your application will + * use in the worst case. + * + * @param[in] sock The socket descriptor on which to set the listening options. + * @param[in] backlog The max length of the queue of pending connections. A value of 0 means + * infinite. + * + * @return 0 on success, or -1 on error. + */ +int listen(int sock, int backlog); + +/** + * @brief Function for waiting for the next client to connect. + * + * @details This function will block if there are no clients attempting to connect. + * + * @param[in] sock The socket descriptor to use for waiting on client connections. + * @param[out] p_cliaddr Socket address that will be set to the client's address. + * @param[out] p_addrlen The size of the p_cliaddr passed. Might be modified by the function. + * + * @return A non-negative client descriptor on success, or -1 on error. + */ +int accept(int sock, void * p_cliaddr, socklen_t * p_addrlen); + +/** + * @brief Function for converting a human-readable IP address to a form usable by the socket API. + * + * @details This function will convert a string form of addresses and encode it into a byte array. + * + * @param[in] af Address family. Only AF_INET6 supported. + * @param[in] p_src Null-terminated string containing the address to convert. + * @param[out] p_dst Pointer to a struct in6_addr where the address will be stored. + * + * @return 1 on success, 0 if src does not contain a valid address, -1 if af is not a valid address + * family. + */ +int inet_pton(socket_family_t af, const char * p_src, void * p_dst); + +#ifdef __cplusplus +} +#endif + +#endif //SOCKET_API_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/sys/select.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/sys/select.h new file mode 100644 index 0000000..3fb05fb --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/sys/select.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef SOCKET_SYS_SELECT_H__ +#define SOCKET_SYS_SELECT_H__ + +#include "socket_api.h" + +#endif diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/sys/socket.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/sys/socket.h new file mode 100644 index 0000000..c01fc3e --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/sys/socket.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef SOCKET_SYS_SOCKET_H__ +#define SOCKET_SYS_SOCKET_H__ + +#include "socket_api.h" + +#endif diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/unistd.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/unistd.h new file mode 100644 index 0000000..ac77bdf --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/api/unistd.h @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file unistd.h + * + * @defgroup iot_posix_unistd POSIX operating system API + * @ingroup iot_sdk_socket + * @{ + * @brief POSIX standard functions. + */ +#ifndef SOCKET_UNISTD_H__ +#define SOCKET_UNISTD_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief POSIX sleep function. + * + * @note Uses busy looping nrf_delay. + * + * @param[in] seconds The number of seconds to sleep. + * + * @return The number of seconds slept. + */ +unsigned int sleep(unsigned int seconds); + +/** + * @brief POSIX usleep function. + * + * @note Uses busy looping nrf_delay. + * + * @param[in] useconds The number of microseconds to sleep. + * + * @return The number of microseconds slept. + */ +unsigned int usleep(unsigned int useconds); + +#ifdef __cplusplus +} +#endif + +#endif // SOCKET_UNISTD_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/sleep.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/sleep.c new file mode 100644 index 0000000..94dd07a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/sleep.c @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "unistd.h" +#include "nrf_delay.h" + +unsigned int sleep(unsigned int seconds) +{ + nrf_delay_ms(seconds * 1000); + return seconds; +} + +unsigned int usleep(unsigned int useconds) +{ + nrf_delay_ms(useconds / 1000); + return useconds; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket.c new file mode 100644 index 0000000..fe7d6ca --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket.c @@ -0,0 +1,746 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "nordic_common.h" +#include "sdk_common.h" +#include "sdk_config.h" +#include "nrf_sdm.h" +#include "app_scheduler.h" +#include "app_timer.h" +#include "iot_common.h" +#include "app_error.h" +#include "socket_api.h" +#include "socket_common.h" +#include "socket_trace.h" +#include "sdk_os.h" +#include "transport_if.h" +#include "portdb.h" +#include "errno.h" +#include "mem_manager.h" +#include "ipv6_parse.h" +#include "netinet/in.h" +#include "unistd.h" +#include "sdk_os.h" +#include "nrf_log_ctrl.h" +#include "nrf_log_default_backends.h" + +#ifndef SOCKET_ENABLE_API_PARAM_CHECK +#define SOCKET_ENABLE_API_PARAM_CHECK 0 +#endif + +#include "socket_config.h" + +#if SOCKET_CONFIG_LOG_ENABLED == 1 + NRF_LOG_MODULE_REGISTER(); +#endif + +/** + * @defgroup api_param_check API Parameters check macros. + * + * @details Macros that verify parameters passed to the module in the APIs. These macros + * could be mapped to nothing in final versions of code to save execution and size. + * SOCKET_ENABLE_API_PARAM_CHECK should be set to 0 to disable these checks. + * + * @{ + */ +#if SOCKET_ENABLE_API_PARAM_CHECK == 1 + +/**@brief Macro to check is module is initialized before requesting one of the module procedures. */ +#define VERIFY_MODULE_IS_INITIALIZED() \ + do { \ + if (m_initialization_state == false) \ + { \ + return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_SOCKET_ERR_BASE);\ + } \ + } while (0) + + +/** + * @brief Verify NULL parameters are not passed to API by application. + */ +#define NULL_PARAM_CHECK(PARAM) \ + do { \ + if ((PARAM) == NULL) \ + { \ + set_errno(EFAULT); \ + return -1; \ + } \ + } while (0) + +/** + * @brief Verify socket id passed on the API by application is valid. + */ +#define VERIFY_SOCKET_ID(ID) \ + do { \ + if (((ID) < 0) || ((ID) >= NUM_SOCKETS)) \ + { \ + set_errno(EBADF); \ + return -1; \ + } \ + } while (0) + + +#else + +#define VERIFY_MODULE_IS_INITIALIZED() +#define NULL_PARAM_CHECK(PARAM) +#define VERIFY_SOCKET_ID(ID) +#endif + +/** @} */ +#define SOCKET_MUTEX_INIT() SDK_MUTEX_INIT(m_socket_mtx); +#define SOCKET_MUTEX_LOCK() SDK_MUTEX_LOCK(m_socket_mtx) +#define SOCKET_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_socket_mtx) +// note: one extra for configuration socket +#define NUM_SOCKETS SOCKET_MAX_SOCKET_COUNT + 1 + +SDK_MUTEX_DEFINE(m_socket_mtx) /**< Mutex for protecting m_socket_table (not individual entries). */ + +#define SCHED_QUEUE_SIZE 16 /**< Maximum number of events in the scheduler queue. */ +#define SCHED_MAX_EVENT_DATA_SIZE 192 /**< Maximum size of scheduler events. */ + +static bool m_initialization_state = false; /**< Variable to maintain module initialization state. */ +static volatile bool m_interface_up = false; /**< Interface state. */ +static socket_t m_socket_table[NUM_SOCKETS]; /**< Socket table. */ + +const struct in6_addr in6addr_any = { {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, /**< IPv6 anycast address. */ + 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u} }; + +#if defined (NRF_LOG_ENABLED) && (NRF_LOG_ENABLED == 1) + +void log_init(void) +{ + ret_code_t err_code = NRF_LOG_INIT(NULL); + APP_ERROR_CHECK(err_code); + + NRF_LOG_DEFAULT_BACKENDS_INIT(); +} + +#else // defined (NRF_LOG_ENABLED) && (NRF_LOG_ENABLED == 1) + +void log_init(void) +{ + ; +} + +#endif // defined (NRF_LOG_ENABLED) && (NRF_LOG_ENABLED == 1) + +uint32_t socket_init(void) +{ + memset(m_socket_table, 0, sizeof(m_socket_table)); + + SOCKET_MUTEX_INIT(); + + log_init(); + + uint32_t err_code = nrf_mem_init(); + APP_ERROR_CHECK(err_code); + + APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE); + + err_code = app_timer_init(); + APP_ERROR_CHECK(err_code); + + err_code = config_socket_init(); + APP_ERROR_CHECK(err_code); + +#if SOCKET_TRANSPORT_ENABLE == 1 + err_code = portdb_init(SOCKET_MAX_SOCKET_COUNT); + APP_ERROR_CHECK(err_code); + + transport_handler_init(); +#endif + config_socket_start(); + m_initialization_state = true; + + SOCKET_TRACE("Socket init complete"); + + return NRF_SUCCESS; +} + +/** + * Finds a free entry in the socket table, marks it as used and returns it. Returns -1 if no entry + * was found. + */ +static int socket_allocate(socket_t ** pp_socket) +{ + int ret_sock = -1; + SOCKET_MUTEX_LOCK(); + for (int sock = 0; sock < NUM_SOCKETS; sock++) + { + SOCKET_TRACE("Looking at socket %d with state %d", (int)sock, m_socket_table[sock].so_state); + if (m_socket_table[sock].so_state == STATE_CLOSED) + { + m_socket_table[sock].so_state = STATE_OPEN; + ret_sock = sock; + *pp_socket = &m_socket_table[sock]; + break; + } + } + if (ret_sock < 0) + { + set_errno(EMFILE); + } + SOCKET_MUTEX_UNLOCK(); + return ret_sock; +} + +static socket_t * socket_find(int sock) +{ + SOCKET_MUTEX_LOCK(); + socket_t * p_socket = &m_socket_table[sock]; + SOCKET_MUTEX_UNLOCK(); + return p_socket; +} + +static void socket_free(int sock) +{ + SOCKET_TRACE("Freeing socket %d", (int)sock); + SOCKET_MUTEX_LOCK(); + memset(&m_socket_table[sock], 0, sizeof(m_socket_table[sock])); + m_socket_table[sock].so_state = STATE_CLOSED; + SOCKET_MUTEX_UNLOCK(); +} + +#if SOCKET_TRANSPORT_ENABLE == 1 +void transport_interface_up(void) +{ + m_interface_up = true; +} + +void transport_interface_down(void) +{ + m_interface_up = false; + for (int sock = 0; sock < NUM_SOCKETS; sock++) + { + (void) close(sock); + } +} +#endif + +int fcntl(int fd, int cmd, int flags) +{ + VERIFY_MODULE_IS_INITIALIZED(); + VERIFY_SOCKET_ID(fd); + + if (!((cmd == F_SETFL) || (cmd == F_GETFL))) + { + set_errno(EINVAL); + return -1; + } + socket_t * p_socket = socket_find(fd); + + if (cmd == F_SETFL) + { + p_socket->so_flags = flags; + } + else if (cmd == F_GETFL) + { + return p_socket->so_flags; + } + + return 0; +} + +static void socket_set_errno(uint32_t err_code) +{ + switch (err_code) { + case UDP_INTERFACE_NOT_READY: // fallthrough + case SOCKET_INTERFACE_NOT_READY: + set_errno(ENETDOWN); + break; + case SOCKET_WOULD_BLOCK: + set_errno(EAGAIN); + break; + case SOCKET_NO_ROUTE: + set_errno(ENETUNREACH); + break; + case NRF_ERROR_NO_MEM: // fallthrough + case SOCKET_NO_MEM: + set_errno(ENOMEM); + break; + case SOCKET_TIMEOUT: + set_errno(ETIMEDOUT); + break; + case SOCKET_NO_AVAILABLE_PORTS: + set_errno(EMFILE); + break; + case SOCKET_PORT_IN_USE: // fallthrough + case SOCKET_ADDRESS_IN_USE: + set_errno(EADDRINUSE); + break; + case SOCKET_INVALID_PARAM: + set_errno(EINVAL); + break; + case SOCKET_UNSUPPORTED_PROTOCOL: + set_errno(EPROTONOSUPPORT); + break; + case SOCKET_NOT_CONNECTED: + set_errno(ENOTCONN); + break; + } +} + + +int socket(socket_family_t family, socket_type_t type, socket_protocol_t protocol) +{ + if (m_initialization_state == false) + { + (void) socket_init(); + } + VERIFY_MODULE_IS_INITIALIZED(); + + int ret_sock = -1; + socket_t * p_socket = NULL; + int sock = socket_allocate(&p_socket); + SOCKET_TRACE("Got value %d from allocate", (int)sock); + if (sock >= 0) + { + p_socket->so_params.so_family = family; + p_socket->so_params.so_protocol = protocol; + p_socket->so_params.so_type = type; + p_socket->so_transport = NULL; + + if (family == AF_INET6) + { +#if SOCKET_TRANSPORT_ENABLE == 1 + p_socket->so_transport = &transport_impl; +#else + set_errno(EAFNOSUPPORT); +#endif + } + else if (family == AF_NRF_CFG || family == AF_NRF_CFG_INTERNAL) + { + p_socket->so_transport = &config_socket_transport; + } + else + { + set_errno(EAFNOSUPPORT); + } + + if (p_socket->so_transport != NULL) + { + uint32_t err_code = p_socket->so_transport->open(p_socket); + socket_set_errno(err_code); + ret_sock = (err_code == NRF_SUCCESS) ? sock : ret_sock; + } + + if (ret_sock < 0) + { + socket_free(sock); + } + } + SOCKET_TRACE("Returning socket value %d", (int)ret_sock); + return ret_sock; +} + +static uint32_t wait_interface_up(void) +{ + SOCKET_TRACE("Waiting for interface to come up"); + uint32_t err_code = NRF_SUCCESS; + while (err_code == NRF_SUCCESS && m_interface_up == false) + { + err_code = socket_wait(); + } + if (m_interface_up == true) + { + SOCKET_TRACE("Interface is up!"); + } + return err_code; +} + +static uint32_t socket_interface_up(bool is_blocking) +{ + uint32_t err_code = NRF_SUCCESS; + if (m_interface_up == false) + { + if (is_blocking) + { + (void) wait_interface_up(); + } + } + if (m_interface_up == false) + { + err_code = SOCKET_INTERFACE_NOT_READY; + } + return err_code; +} + +int connect(int sock, const void * p_addr, socklen_t addrlen) +{ + VERIFY_MODULE_IS_INITIALIZED(); + VERIFY_SOCKET_ID(sock); + NULL_PARAM_CHECK(p_addr); + + socket_t * p_socket = socket_find(sock); + bool is_blocking = ((p_socket->so_flags & O_NONBLOCK) == 0); + int ret = -1; + + uint32_t err_code = socket_interface_up(is_blocking); + if (err_code != NRF_SUCCESS) + { + socket_set_errno(err_code); + } + else if (p_socket->so_state == STATE_OPEN) + { + err_code = p_socket->so_transport->connect(p_socket, p_addr, addrlen); + if (err_code == NRF_SUCCESS) + { + p_socket->so_state = STATE_CONNECTED; + ret = 0; + } + socket_set_errno(err_code); + } + else if (p_socket->so_state == STATE_CONNECTED) + { + set_errno(EISCONN); + } + else if (p_socket->so_state == STATE_CLOSED) + { + set_errno(EBADF); + } + return ret; +} + + +ssize_t sendto(int sock, + const void * p_buf, + size_t buflen, + int flags, + const void * p_servaddr, + socklen_t addrlen) +{ + VERIFY_MODULE_IS_INITIALIZED(); + VERIFY_SOCKET_ID(sock); + NULL_PARAM_CHECK(p_buf); + + socket_t * p_socket = socket_find(sock); + + if ((p_socket->so_flags & O_NONBLOCK) != 0 && + (flags & MSG_WAITALL) == 0) + { + flags |= MSG_DONTWAIT; + } + + uint32_t err_code = socket_interface_up(((p_socket->so_flags & O_NONBLOCK) == 0) || ((flags & MSG_DONTWAIT) == 0)); + + ssize_t ret = -1; + if (err_code == NRF_SUCCESS) + { + err_code = p_socket->so_transport->send(p_socket, p_buf, buflen, flags, p_servaddr, addrlen); + if (err_code == NRF_SUCCESS) + { + ret = (ssize_t) buflen; + } + } + socket_set_errno(err_code); + return ret; +} + +ssize_t send(int sock, const void * p_buf, size_t buflen, int flags) +{ + return sendto(sock, p_buf, buflen, flags, NULL, 0); +} + +ssize_t write(int sock, const void * p_buf, size_t buflen) +{ + return send(sock, p_buf, buflen, 0); +} + +ssize_t recvfrom(int sock, + void * p_buf, + size_t buf_size, + int flags, + void * p_cliaddr, + socklen_t * p_addrlen) +{ + VERIFY_MODULE_IS_INITIALIZED(); + VERIFY_SOCKET_ID(sock); + NULL_PARAM_CHECK(p_buf); + + socket_t * p_socket = socket_find(sock); + ssize_t ret = -1; + uint32_t recv_size = buf_size; + + uint32_t err_code = p_socket->so_transport->recv(p_socket, + p_buf, + &recv_size, + flags, + p_cliaddr, + p_addrlen); + if (err_code == NRF_SUCCESS) + { + ret = (ssize_t) recv_size; + } + socket_set_errno(err_code); + return ret; +} + +ssize_t recv(int sock, void * p_buf, size_t buf_size, int flags) +{ + return recvfrom(sock, p_buf, buf_size, flags, NULL, NULL); +} + +ssize_t read(int sock, void * p_buf, size_t buf_size) +{ + return recv(sock, p_buf, buf_size, 0); +} + +int setsockopt(int sock, + socket_opt_lvl_t level, + int optname, + const void * p_optval, + socklen_t optlen) +{ + VERIFY_MODULE_IS_INITIALIZED(); + VERIFY_SOCKET_ID(sock); + + socket_t * p_socket = socket_find(sock); + + uint32_t err_code = p_socket->so_transport->setsockopt(p_socket, + level, + optname, + p_optval, + optlen); + socket_set_errno(err_code); + return (err_code == NRF_SUCCESS ? 0 : -1); +} + +int getsockopt(int sock, socket_opt_lvl_t level, int optname, void * p_optval, socklen_t * p_optlen) +{ + VERIFY_MODULE_IS_INITIALIZED(); + VERIFY_SOCKET_ID(sock); + + socket_t * p_socket = socket_find(sock); + + uint32_t err_code = p_socket->so_transport->getsockopt(p_socket, + level, + optname, + p_optval, + p_optlen); + socket_set_errno(err_code); + return (err_code == NRF_SUCCESS ? 0 : -1); +} + +int bind(int sock, const void * p_addr, socklen_t addrlen) +{ + VERIFY_MODULE_IS_INITIALIZED(); + VERIFY_SOCKET_ID(sock); + NULL_PARAM_CHECK(p_addr); + + socket_t * p_socket = socket_find(sock); + bool is_blocking = ((p_socket->so_flags & O_NONBLOCK) == 0); + int ret = -1; + + uint32_t err_code = socket_interface_up(is_blocking); + if (err_code == NRF_SUCCESS) + { + err_code = p_socket->so_transport->bind(p_socket, p_addr, addrlen); + } + if (err_code == NRF_SUCCESS) + { + ret = 0; + } + socket_set_errno(err_code); + return ret; +} + +int listen(int sock, int backlog) +{ + VERIFY_MODULE_IS_INITIALIZED(); + VERIFY_SOCKET_ID(sock); + + socket_t * p_socket = socket_find(sock); + + uint32_t err_code = p_socket->so_transport->listen(p_socket, backlog); + return (err_code == NRF_SUCCESS ? 0 : -1); +} + +int accept(int sock, void * p_cliaddr, socklen_t * p_addrlen) +{ + VERIFY_MODULE_IS_INITIALIZED(); + VERIFY_SOCKET_ID(sock); + NULL_PARAM_CHECK(p_cliaddr); + NULL_PARAM_CHECK(p_addrlen); + + socket_t * p_socket = socket_find(sock); + int ret = -1; + + if (p_socket->so_params.so_type != SOCK_STREAM) + { + set_errno(EOPNOTSUPP); + } + else + { + uint32_t err_code = NRF_SUCCESS; + socket_t * p_client = NULL; + int sock_cli = socket_allocate(&p_client); + if (sock_cli >= 0) + { + p_client->so_params = p_socket->so_params; + p_client->so_state = STATE_CONNECTED; + p_client->so_transport = p_socket->so_transport; + err_code = p_socket->so_transport->accept(p_socket, p_client, p_cliaddr, p_addrlen); + } + + if (err_code == NRF_SUCCESS) + { + ret = sock_cli; + } + else + { + socket_set_errno(err_code); + socket_free(sock_cli); + } + } + return ret; +} + +int close(int sock) +{ + VERIFY_MODULE_IS_INITIALIZED(); + VERIFY_SOCKET_ID(sock); + + socket_t * p_socket = socket_find(sock); + int ret = 0; + + if (p_socket->so_state != STATE_CLOSED) + { + uint32_t err_code = p_socket->so_transport->close(p_socket); + ret = (err_code == NRF_SUCCESS) ? 0 : -1; + SOCKET_TRACE("Close socket %d: ret: %d", (int)sock, ret); + socket_free(sock); + } + return ret; +} + +int fd_set_cmp(fd_set * set_a, fd_set * set_b) +{ + int ret = 0; + if (set_a != NULL && set_b != NULL) + { + for (uint32_t i = 0; i < FD_SETSIZE; i++) + { + if (FD_ISSET(i, set_a) != FD_ISSET(i, set_b)) + { + ret = 1; + break; + } + } + } + return ret; +} + +int select(int nfds, + fd_set * p_readset, + fd_set * p_writeset, + fd_set * p_exceptset, + const struct timeval * p_timeout) +{ + VERIFY_SOCKET_ID(nfds - 1); + + // Approximately 10 ms sleep between each iteration + uint32_t timestep = 10000; + uint32_t endtime = 0; + if (p_timeout != NULL) + { + endtime = (p_timeout->tv_sec * 1000000) + p_timeout->tv_usec; + } + fd_set readset; + FD_ZERO(&readset); + fd_set writeset; + FD_ZERO(&writeset); + fd_set exceptset; + FD_ZERO(&exceptset); + +#define SELECT_CHECK_SET(in_set, out_set, evt_var) \ + if ((in_set) != NULL) \ + { \ + if (FD_ISSET(sock, (in_set)) && (evt_var) > 0) \ + { \ + FD_SET(sock, (out_set)); \ + num_ready++; \ + } \ + else \ + { \ + FD_CLR(sock, (out_set)); \ + } \ + } + + int num_ready = 0; + uint32_t err_code = NRF_SUCCESS; + while (err_code == NRF_SUCCESS) + { + for (int sock = 0; sock < nfds; sock++) + { + socket_t * p_socket = socket_find(sock); + SELECT_CHECK_SET(p_readset, &readset, p_socket->so_read_evt); + SELECT_CHECK_SET(p_writeset, &writeset, p_socket->so_write_evt); + SELECT_CHECK_SET(p_exceptset, &exceptset, p_socket->so_except_evt); + } + // TODO: Check out how app events queue up while we checked the socket + if (fd_set_cmp(p_readset, &readset) == 0 && + fd_set_cmp(p_writeset, &writeset) == 0 && + fd_set_cmp(p_exceptset, &exceptset) == 0) + + { + break; + } + else + { + if (p_timeout == NULL) + { + err_code = socket_wait(); + } + else if (endtime - timestep < endtime) + { + (void) usleep(timestep); + endtime -= timestep; + } + else + { + break; + } + } + + } + + return num_ready; +} + diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket_common.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket_common.h new file mode 100644 index 0000000..d9a0f8a --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket_common.h @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file socket_common.h + * + * @defgroup socket_common BSD Socket internal functions and structures + * @ingroup iot_sdk_socket + * @{ + * @brief Nordic socket interface internal functions and structures. + */ +#ifndef SOCKET_COMMON_H__ +#define SOCKET_COMMON_H__ + +#include <stdint.h> +#include "socket_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct socket_transport; + +#define AF_NRF_CFG_INTERNAL 41 /**< Socket family type, internal NRF configuration socket. */ + +/** + * @brief Function for initializing the socket API module. + * + * @return NRF_SUCCESS on success, otherwise error code is returned. + */ +uint32_t socket_init(void); + +/** + * @brief Waiting function for sockets. + * + * @details Must be implemented by specific modules such as BLEs. + * + * @return NRF_SUCCESS on success, otherwise error code is returned. + */ +uint32_t socket_wait(void); + +/** + * @brief Create parameters for a socket. + */ +typedef struct { + socket_family_t so_family; /**< Socket family. */ + socket_protocol_t so_protocol; /**< Socket protocol. */ + socket_type_t so_type; /**< Socket type. */ +} socket_params_t; + +/** + * @brief Different states a socket can be in. + */ +typedef enum { + STATE_CLOSED = 0, /**< Socket is closed. */ + STATE_OPEN, /**< Socket is opened. */ + STATE_CONNECTED, /**< Socket is connected. */ +} socket_state_t; + +/** + * @brief The state associated with a socket handle. + */ +typedef struct { + socket_params_t so_params; /**< Generic socket parameters. */ + void * so_ctx; /**< Transport specific context. */ + int so_flags; /**< Socket flags. */ + uint16_t so_read_evt; /**< Notifying of read events. */ + uint16_t so_write_evt; /**< Notifying of write events. */ + uint16_t so_except_evt; /**< Notifying of exceptional events. */ + struct socket_transport * so_transport; /**< Transport attached to this socket. */ + volatile socket_state_t so_state; /**< Socket state. */ +} socket_t; + +#ifdef __cplusplus +} +#endif + +#endif // SOCKET_COMMON_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket_config.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket_config.h new file mode 100644 index 0000000..cce571e --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket_config.h @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file socket_config.h + * + * @defgroup iot_socket_config Configuration socket API + * @ingroup iot_sdk_socket + * @{ + * @brief Configuration socket API. + * + * This API is used internally by the socket handling to support the configuration + * socket type. The configuration socket is implemented using a special transport hook, which is + * implemented differently depending on the platform. + */ +#ifndef SOCKET_CONFIG_H__ +#define SOCKET_CONFIG_H__ + +#include "socket_common.h" +#include "transport_if.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Function for setting the default configuration. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +uint32_t config_socket_init(void); + +/** + * @brief Function for starting the configuration layer. + */ +void config_socket_start(void); + +extern socket_transport_t config_socket_transport; + +#ifdef __cplusplus +} +#endif + +#endif // SOCKET_CONFIG_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket_trace.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket_trace.h new file mode 100644 index 0000000..87fafdf --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/socket_trace.h @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file socket_trace.h + * + * @defgroup iot_socket_debug_log Module's log macros + * @ingroup iot_sdk_socket + * @{ + * @brief Socket trace macros. + * + * @details Macros for creating module logs which can be useful in understanding the handling + * of events or actions on API requests. These are intended for debugging purposes and + * can be disabled by defining the SOCKET_CONFIG_LOG_ENABLED. + * @note If NRF_LOG_ENABLED is disabled, SOCKET_CONFIG_LOG_ENABLED has no effect. + */ +#ifndef SOCKET_TRACE_H__ +#define SOCKET_TRACE_H__ + +#include "sdk_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOCKET_CONFIG_LOG_ENABLED == 1 +#if defined (NRF_LOG_ENABLED) && (NRF_LOG_ENABLED == 1) + +#define NRF_LOG_MODULE_NAME socket + +#define NRF_LOG_LEVEL SOCKET_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR SOCKET_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR SOCKET_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" + +#define SOCKET_TRACE NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */ +#define SOCKET_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */ +#define SOCKET_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */ + +#else + +#define SOCKET_TRACE(...) \ + do { \ + (void) fprintf( stderr, "socket: " ); \ + (void) fprintf( stderr, __VA_ARGS__ ); \ + (void) fprintf( stderr, "\r\n" ); \ + } while (0) + +#endif + +#else // SOCKET_CONFIG_LOG_ENABLED + +#define SOCKET_TRACE(...) /**< Disables traces. */ +#define SOCKET_ERR(...) /**< Disables error logs. */ +#define SOCKET_DUMP(...) /**< Disables dumping of octet streams. */ + +#endif // SOCKET_CONFIG_LOG_ENABLED + + +#ifdef __cplusplus +} +#endif + +#endif // SOCKET_TRACE_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/transport_if.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/transport_if.h new file mode 100644 index 0000000..75a86c1 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/common/transport_if.h @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file transport_if.h + * + * @defgroup iot_socket_transport_if Transport implementation interface + * @ingroup iot_sdk_socket + * @{ + * @brief Transport implementation interface. + * + * The transport interface defines the hook to be used for socket transport. The implementation of + * this interface would be different across platforms and event IP stacks (i.e. Nordic IPv6 vs LwIP). + */ +#ifndef TRANSPORT_IF_H__ +#define TRANSPORT_IF_H__ + +#include "iot_defines.h" +#include "socket_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Function for opening a socket. + */ +typedef uint32_t (*tr_open_t) (socket_t * p_socket); + +/** + * @brief Function for connecting a socket to an address. + */ +typedef uint32_t (*tr_connect_t)(socket_t * p_socket, const void * p_addr, socklen_t addrlen); + +/** + * @brief Function for sending data on a socket. + */ +typedef uint32_t (*tr_send_t)(socket_t * p_socket, + const void * p_buf, + uint32_t len, + int flags, + const void * p_destaddr, + socklen_t destaddr_len); + +/** + * @brief Function for receiving data from a socket. + */ +typedef uint32_t (*tr_recv_t)(socket_t * p_socket, + void * p_buf, + uint32_t * sz, + int flags, + void * p_srcaddr, + socklen_t * p_srcaddr_len); + +/** + * @brief Function for binding a socket to an address and port. + */ +typedef uint32_t (*tr_bind_t)(socket_t * p_socket, const void * p_addr, socklen_t addrlen); + +/** + * @brief Function for listening on a socket. + */ +typedef uint32_t (*tr_listen_t)(socket_t * p_socket, int backlog); + +/** + * @brief Function for accepting a connection on a socket. + */ +typedef uint32_t (*tr_accept_t)(socket_t * p_socket, + socket_t * p_client, + void * p_client_addr, + socklen_t * p_client_addr_len); + +/** + * @brief Function for closing a socket. + */ +typedef uint32_t (*tr_close_t)(socket_t * p_socket); + +/** + * @brief Function for setting options on a socket. + */ +typedef uint32_t (*tr_setsockopt_t)(socket_t * p_socket, + int level, + int optname, + const void * p_optval, + socklen_t optlen); + +/** + * @brief Function for getting options from a socket. + */ +typedef uint32_t (*tr_getsockopt_t)(socket_t * p_socket, + int level, + int optname, + void * p_optval, + socklen_t * p_optlen); + +/** + * @brief The transport interface. + */ +typedef struct socket_transport +{ + tr_open_t open; /**< Open a socket. */ + tr_connect_t connect; /**< Connect a socket to an address. */ + tr_send_t send; /**< Send data on a socket. */ + tr_recv_t recv; /**< Receive data from a socket. */ + tr_bind_t bind; /**< Bind a socket to an address and port. */ + tr_listen_t listen; /**< Listen on a socket. */ + tr_accept_t accept; /**< Accept connection on a socket. */ + tr_close_t close; /**< Close a socket. */ + tr_setsockopt_t setsockopt; /**< Set options on a socket. */ + tr_getsockopt_t getsockopt; /**< Get options from a socket. */ +} socket_transport_t; + +#if SOCKET_TRANSPORT_ENABLE == 1 +extern socket_transport_t transport_impl; +void transport_handler_init(void); +void transport_interface_up(void); +void transport_interface_down(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // TRANSPORT_IF_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/config/medium/config_medium.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/config/medium/config_medium.c new file mode 100644 index 0000000..6b883dd --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/config/medium/config_medium.c @@ -0,0 +1,227 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_config.h" +#include "socket_api.h" +#include "config_medium.h" +#include "ipv6_medium.h" +#include "iot_errors.h" +#include "app_error.h" +#include "socket_config.h" + +static ipv6_medium_instance_t m_ipv6_medium; /**< IPv6 medium instance. */ +eui64_t eui64_local_iid; /**< Local EUI64 value that is used as the IID for*/ + +eui64_t * config_medium_local_iid(void) +{ + return &eui64_local_iid; +} + +void config_medium_start(void) +{ + uint32_t err_code = ipv6_medium_connectable_mode_enter(m_ipv6_medium.ipv6_medium_instance_id); + APP_ERROR_CHECK(err_code); +} + +static void on_ipv6_medium_evt(ipv6_medium_evt_t * p_ipv6_medium_evt) +{ + switch (p_ipv6_medium_evt->ipv6_medium_evt_id) + { + case IPV6_MEDIUM_EVT_CONN_UP: + { + // TODO: Signal + break; + } + case IPV6_MEDIUM_EVT_CONN_DOWN: + { + config_medium_start(); + break; + } + default: + { + break; + } + } +} + + +static void on_ipv6_medium_error(ipv6_medium_error_t * p_ipv6_medium_error) +{ + // Do something. +} + +static uint32_t config_medium_init(ipv6_medium_init_params_t * p_medium_params, + ipv6_medium_type_t medium_type) +{ + uint32_t err_code = ipv6_medium_init(p_medium_params, medium_type, &m_ipv6_medium); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + eui48_t ipv6_medium_eui48; + err_code = ipv6_medium_eui48_get(m_ipv6_medium.ipv6_medium_instance_id, &ipv6_medium_eui48); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + ipv6_medium_eui48.identifier[EUI_48_SIZE - 1] = 0x00; + + err_code = ipv6_medium_eui48_set(m_ipv6_medium.ipv6_medium_instance_id, &ipv6_medium_eui48); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + err_code = ipv6_medium_eui64_get(m_ipv6_medium.ipv6_medium_instance_id, &eui64_local_iid); + return err_code; +} + +uint32_t config_medium_init_default(void) +{ + static ipv6_medium_init_params_t ipv6_medium_init_params; + memset(&ipv6_medium_init_params, 0x00, sizeof(ipv6_medium_init_params)); + ipv6_medium_init_params.ipv6_medium_evt_handler = on_ipv6_medium_evt; + ipv6_medium_init_params.ipv6_medium_error_handler = on_ipv6_medium_error; + return config_medium_init(&ipv6_medium_init_params, IPV6_MEDIUM_ID_BLE); +} + +uint32_t config_medium_setopt(int optname, const void * p_optarg, socklen_t optlen) +{ + uint32_t err_code = SOCKET_INVALID_PARAM; + if (optname == MEDIUM_INIT_PARAMS) + { + if (optlen == sizeof(config_medium_params_t)) + { + const config_medium_params_t * p_cfg = (const config_medium_params_t *)p_optarg; + ipv6_medium_init_params_t ipv6_medium_init_params; + memset(&ipv6_medium_init_params, 0x00, sizeof(ipv6_medium_init_params)); + ipv6_medium_init_params.ipv6_medium_evt_handler = p_cfg->evt_handler; + ipv6_medium_init_params.ipv6_medium_error_handler = p_cfg->error_handler; + err_code = config_medium_init(&ipv6_medium_init_params, p_cfg->medium_type); + } + } + return err_code; +} + +uint32_t config_medium_getopt(int optname, void * p_optarg, socklen_t * p_optlen) +{ + (void) optname; + (void) p_optarg; + (void) p_optlen; + return SOCKET_INVALID_PARAM; +} + +uint32_t config_socket_init(void) +{ + uint32_t err_code = NRF_SUCCESS; +#if SOCKET_AUTOINIT_ENABLE == 1 + err_code = config_medium_init_default(); +#endif + return err_code; +} + +void config_socket_start(void) +{ +#if SOCKET_AUTOINIT_ENABLE == 1 + config_medium_start(); +#endif +} + +static uint32_t config_socket_open(socket_t * p_socket) +{ + (void) p_socket; + return NRF_ERROR_NULL; +} + +static uint32_t config_socket_close(socket_t * p_socket) +{ + (void) p_socket; + return NRF_ERROR_NULL; +} + +uint32_t config_socket_setopt(socket_t * p_socket, + int level, + int optname, + const void * p_optarg, + socklen_t optlen) +{ + uint32_t err_code = NRF_SUCCESS; + switch (level) + { + case SOL_NRF_MEDIUM: + err_code = config_medium_setopt(optname, p_optarg, optlen); + break; + default: + err_code = SOCKET_INVALID_PARAM; + break; + } + return err_code; +} + +uint32_t config_socket_getopt(socket_t * p_socket, + int level, + int optname, + void * p_optarg, + socklen_t * p_optlen) +{ + uint32_t err_code = NRF_SUCCESS; + switch (level) + { + case SOL_NRF_MEDIUM: + err_code = config_medium_getopt(optname, p_optarg, p_optlen); + break; + default: + err_code = SOCKET_INVALID_PARAM; + break; + } + return err_code; +} + +/** + * @brief Transport for configuration socket. + */ +socket_transport_t config_socket_transport = +{ + .open = config_socket_open, + .setsockopt = config_socket_setopt, + .getsockopt = config_socket_getopt, + .close = config_socket_close +}; diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/config/medium/config_medium.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/config/medium/config_medium.h new file mode 100644 index 0000000..e4b7c01 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/config/medium/config_medium.h @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file config_medium.h + * + * @defgroup iot_socket_config_medium Configuration socket based on ipv6_medium + * @ingroup iot_sdk_socket + * @{ + * @brief Configuration socket based on the ipv6_medium module. + * + * This module wraps the medium module in a configuration socket API. + */ +#ifndef CONFIG_MEDIUM_H__ +#define CONFIG_MEDIUM_H__ + +#include <stdint.h> +#include "iot_defines.h" +#include "ipv6_medium.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Function for initializing a configuration socket with default settings. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +uint32_t config_medium_init_default(void); + +/** + * @brief Function for starting the medium layer. + * + * For BLE, this means to start advertising. + */ +void config_medium_start(void); + +/** + * @brief Function for retrieving local interface ID assigned. + * + * @return Pointer to location of interface ID. + */ +eui64_t * config_medium_local_iid(void); + +/** + * @brief Function for setting configuration parameters for the medium layers using the socket option. + * + * @param optname Option name/type. + * @param p_optarg Pointer to option value. + * @param optlen Length of option value. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +uint32_t config_medium_setopt(int optname, const void * p_optarg, socklen_t optlen); + +/** + * @brief Function for getting configuration parameters for the medium layers using the socket option. + * + * @param optname Option name/type. + * @param p_optarg Pointer to the option value structure where the value should be stored. + * @param p_optlen Length of option value. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +uint32_t config_medium_getopt(int optname, void * p_optarg, socklen_t * p_optlen); + +/** + * @brief Parameters passed for the MEDIUM_INIT_PARAMS option. + * + * These are currently the same as for ipv6_medium initialization. + */ +typedef struct { + ipv6_medium_evt_handler_t evt_handler; /**< The medium event handler callback. */ + ipv6_medium_error_handler_t error_handler; /**< The medium error handler callback. */ + ipv6_medium_type_t medium_type; /**< The medium type. */ +} config_medium_params_t; + +#ifdef __cplusplus +} +#endif + +#endif // CONFIG_MEDIUM_H__ + +/**@} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/addr_util/inet_pton.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/addr_util/inet_pton.c new file mode 100644 index 0000000..2fd1f4c --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/addr_util/inet_pton.c @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "socket_api.h" +#include "ipv6_parse.h" + +int inet_pton(socket_family_t af, const char * p_src, void * p_dst) +{ + int ret_val = 1; + if (af != AF_INET6) + { + ret_val = -1; + } + else + { + in6_addr_t * p_addr = (in6_addr_t *)p_dst; + uint32_t err_code = ipv6_parse_addr(p_addr->s6_addr, p_src, strlen(p_src)); + if (err_code != NRF_SUCCESS) + { + ret_val = 0; + } + } + return ret_val; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/fifo/nrf_fifo.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/fifo/nrf_fifo.c new file mode 100644 index 0000000..1458b01 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/fifo/nrf_fifo.c @@ -0,0 +1,176 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "iot_defines.h" +#include "iot_errors.h" +#include "nrf_fifo.h" +#include "mem_manager.h" +#include "nrf.h" + +static __INLINE uint32_t fifo_inc(nrf_fifo_t * p_fifo, uint32_t pos) +{ + return (pos + 1) % p_fifo->nmemb; +} + +static __INLINE bool fifo_full(nrf_fifo_t * p_fifo) +{ + return fifo_inc(p_fifo, p_fifo->write_pos) == p_fifo->read_pos; +} + +static __INLINE bool fifo_empty(nrf_fifo_t * p_fifo) +{ + return p_fifo->read_pos == p_fifo->write_pos; +} + +static __INLINE void fifo_enq(nrf_fifo_t * p_fifo, void * p_ctx) +{ + p_fifo->pp_elements[p_fifo->write_pos] = p_ctx; + __DSB(); + p_fifo->write_pos = fifo_inc(p_fifo, p_fifo->write_pos); +} + + +static __INLINE void fifo_deq(nrf_fifo_t * p_fifo, void ** pp_ctx) +{ + *pp_ctx = p_fifo->pp_elements[p_fifo->read_pos]; + __DSB(); + p_fifo->read_pos = fifo_inc(p_fifo, p_fifo->read_pos); +} + +uint32_t nrf_fifo_init(nrf_fifo_t * p_fifo, uint32_t nmemb, fifo_wait_fn wait_fn, fifo_flush_fn flush_fn) +{ + uint32_t err_code = NRF_SUCCESS; + uint32_t nmemb_actual = nmemb + 1; // Required to allow detection of empty and full state + p_fifo->pp_elements = nrf_malloc(nmemb_actual * sizeof(void *)); + + if (p_fifo->pp_elements == NULL) + { + err_code = NRF_ERROR_NO_MEM; + } + else + { + p_fifo->nmemb = nmemb_actual; + p_fifo->wait = wait_fn; + p_fifo->flush = flush_fn; + p_fifo->read_pos = 0; + p_fifo->write_pos = 0; + } + return err_code; +} + +void nrf_fifo_deinit(nrf_fifo_t * p_fifo) +{ + if (p_fifo->flush != NULL) + { + void * p_data; + uint32_t err_code = nrf_fifo_deq(p_fifo, &p_data, false); + while (err_code == NRF_SUCCESS) + { + p_fifo->flush(p_data); + err_code = nrf_fifo_deq(p_fifo, &p_data, false); + } + } + nrf_free(p_fifo->pp_elements); + p_fifo->nmemb = 0; + p_fifo->read_pos = 0; + p_fifo->write_pos = 0; + p_fifo->wait = NULL; + p_fifo->flush = NULL; +} + +uint32_t nrf_fifo_enq(nrf_fifo_t * p_fifo, void * p_ctx, bool wait) +{ + uint32_t err_code = NRF_SUCCESS; + + if (fifo_full(p_fifo) == true) + { + if (wait == false || p_fifo->wait == NULL) + { + err_code = SOCKET_WOULD_BLOCK; + } + else + { + while (fifo_full(p_fifo) == true && err_code == NRF_SUCCESS) + { + err_code = p_fifo->wait(); + } + } + } + else + { + fifo_enq(p_fifo, p_ctx); + } + return err_code; +} + +uint32_t nrf_fifo_deq(nrf_fifo_t * p_fifo, void ** pp_ctx, bool wait) +{ + uint32_t err_code = NRF_SUCCESS; + + if (fifo_empty(p_fifo) == true) + { + if (wait == false || p_fifo->wait == NULL) + { + err_code = SOCKET_WOULD_BLOCK; + } + else + { + while (fifo_empty(p_fifo) == true && err_code == NRF_SUCCESS) + { + err_code = p_fifo->wait(); + } + } + } + + if (err_code == NRF_SUCCESS) + { + fifo_deq(p_fifo, pp_ctx); + } + return err_code; +} + +bool nrf_fifo_empty(nrf_fifo_t * p_fifo) +{ + return fifo_empty(p_fifo); +} + +bool nrf_fifo_full(nrf_fifo_t * p_fifo) +{ + return fifo_full(p_fifo); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/fifo/nrf_fifo.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/fifo/nrf_fifo.h new file mode 100644 index 0000000..c6fb378 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/fifo/nrf_fifo.h @@ -0,0 +1,170 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_FIFO_H__ +#define NRF_FIFO_H__ + +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file nrf_fifo.h + * + * @defgroup iot_socket_fifo FIFO + * @ingroup iot_sdk_socket + * @{ + * @brief A wait-free bounded FIFO of pointers for single producer/single consumer use. + * + * This FIFO is safe to use in single producer/single consumer patterns. In addition, the following + * restrictions apply for init/deinit: + * + * a) nrf_fifo_enq() and nrf_fifo_deq() may only be called after nrf_fifo_init() is called. + * + * b) All calls to nrf_fifo_enq() and nrf_fifo_deq() must be finished and no new calls must be made before nrf_fifo_deinit() is called. + * + * These restrictions must be handled by the user of the module, for instance by using a mutex. + */ + +/** + * @brief Wait function for blocking enqueue/dequeue. + * + * Should return NRF_SUCCESS as long as there are no errors while waiting. + */ +typedef uint32_t (*fifo_wait_fn)(void); + +/** + * @brief Flush function called on deinit. + * + * On deinit, this function will be called with each remaining element in the FIFO as argument. This + * can be used to ensure that memory is deallocated properly. + * + * @param[in] p_data Pointer to data that is flushed from FIFO. + */ +typedef void (*fifo_flush_fn)(void * p_data); + +/** + * @brief FIFO data structure. + */ +typedef struct { + void ** pp_elements; /**< The array of elements in the FIFO. */ + uint32_t nmemb; /**< The number of elements in this FIFO. */ + fifo_wait_fn wait; /**< The wait function used if blocking. */ + fifo_flush_fn flush; /**< The flush function used on deinit. */ + volatile uint32_t read_pos; /**< Read pointer to next element to read. */ + volatile uint32_t write_pos; /**< Write pointer to next element to write. */ +} nrf_fifo_t; + +/** + * @brief Function for initializing the FIFO. + * + * @param[out] p_fifo The FIFO to initialize. + * @param[in] nmemb The maximum number of elements in the FIFO. + * @param[in] wait_fn The wait function to use for blocking enqueue/dequeue. If NULL, the enq/deq + * functions will never block. + * @param[in] flush_fn The flush function to call on deinit. If NULL, the flush function will not + * be called. + * + * @retval NRF_SUCCESS if fifo was initialized successfully. + */ +uint32_t nrf_fifo_init(nrf_fifo_t * p_fifo, uint32_t nmemb, fifo_wait_fn wait_fn, fifo_flush_fn flush_fn); + +/** + * @brief Function for deinitializing the FIFO. + * + * Frees all memory allocated by this FIFO. All elements are removed. If a flush function was + * specified in nrf_fifo_init(), the function will be called for each remaining element in the + * FIFO. + * + * @param[in, out] p_fifo The FIFO to deinitialize. + */ +void nrf_fifo_deinit(nrf_fifo_t * p_fifo); + +/** + * @brief Function for enqueuing an element on the FIFO. + * + * @param[in, out] p_fifo The FIFO to enqueue elements on. + * @param[in] p_ctx The pointer to enqueue. + * @param[in] wait If true, this function will block until the FIFO has available space. Any + * errors returned by this function will be propagated to the caller. + * + * @retval NRF_SUCCESS if the element was queued. + * @retval NRF_ERROR_NO_MEM if wait was set to false and no space was available. + */ +uint32_t nrf_fifo_enq(nrf_fifo_t * p_fifo, void * p_ctx, bool wait); + +/** + * @brief Function for dequeuing an element from the FIFO. + * + * @param[in, out] p_fifo The FIFO to dequeue elements from. + * @param[out] pp_ctx Pointer to where the dequeued element should be stored. + * @param[in] wait If true, this function will block until the FIFO has elements for dequeuing. + * Any errors returned by this function will be propagated to the caller. + * + * @retval NRF_SUCCESS if the element was queued. + * @retval NRF_ERROR_NO_MEM if wait was set to false and no space was available. + */ +uint32_t nrf_fifo_deq(nrf_fifo_t * p_fifo, void ** pp_ctx, bool wait); + +/** + * @brief Function for checking if the FIFO is empty. + * + * @param[in] p_fifo The FIFO to check. + * @return true if empty, false if not. + */ +bool nrf_fifo_empty(nrf_fifo_t * p_fifo); + +/** + * @brief Function for checking if the FIFO is full. + * + * @param[in] p_fifo The FIFO to check. + * @return true if full, false if not. + */ +bool nrf_fifo_full(nrf_fifo_t * p_fifo); + +/**@} */ + +#ifdef __cplusplus +} +#endif + +#endif // NRF_FIFO_H__ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/mbuf/mbuf.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/mbuf/mbuf.c new file mode 100644 index 0000000..d4b0c79 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/mbuf/mbuf.c @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "iot_defines.h" +#include "iot_errors.h" +#include "sdk_config.h" +#include "mem_manager.h" +#include "mbuf.h" +#include "nrf_fifo.h" +#include "socket_common.h" + +uint32_t mbuf_init(mbuf_t * p_mbuf, + mbuf_read_fn read_fn, + mbuf_buf_len_fn buf_len_fn, + mbuf_free_fn free_fn, + uint32_t nmemb) +{ + p_mbuf->p_current = NULL; + p_mbuf->read_pos = 0; + p_mbuf->read = read_fn; + p_mbuf->buf_len = buf_len_fn; + p_mbuf->free = free_fn; + return nrf_fifo_init(&p_mbuf->fifo, nmemb, socket_wait, free_fn); +} + +void mbuf_deinit(mbuf_t * p_mbuf) +{ + if (p_mbuf->p_current != NULL) + { + p_mbuf->free(p_mbuf->p_current); + p_mbuf->p_current = NULL; + } + + p_mbuf->read_pos = 0; + p_mbuf->read = NULL; + p_mbuf->buf_len = NULL; + p_mbuf->free = NULL; + nrf_fifo_deinit(&p_mbuf->fifo); +} + +static bool mbuf_empty_current(mbuf_t * p_mbuf) +{ + return (p_mbuf->buf_len(p_mbuf->p_current) == p_mbuf->read_pos); +} + +bool mbuf_empty(mbuf_t * p_mbuf) +{ + return ((p_mbuf->p_current == NULL || mbuf_empty_current(p_mbuf)) && + nrf_fifo_empty(&p_mbuf->fifo)); +} + +uint32_t mbuf_write(mbuf_t * p_mbuf, void * p_ctx) +{ + return nrf_fifo_enq(&p_mbuf->fifo, p_ctx, false); +} + +static void mbuf_load(mbuf_t * p_mbuf) +{ + if (p_mbuf->p_current == NULL) + { + (void)nrf_fifo_deq(&p_mbuf->fifo, &p_mbuf->p_current, false); + } +} + +uint32_t mbuf_read(mbuf_t * p_mbuf, void * p_buf, uint32_t buf_size) +{ + uint32_t nbytes = 0; + while (nbytes < buf_size && mbuf_empty(p_mbuf) == false) + { + mbuf_load(p_mbuf); + void * p_current = p_mbuf->p_current; + const uint32_t copy_len = p_mbuf->read(p_current, + p_mbuf->read_pos, + ((uint8_t *)p_buf) + nbytes, + buf_size - nbytes); + p_mbuf->read_pos += copy_len; + nbytes += copy_len; + if (mbuf_empty_current(p_mbuf) == true) + { + p_mbuf->free(p_mbuf->p_current); + p_mbuf->p_current = NULL; + p_mbuf->read_pos = 0; + } + } + return nbytes; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/mbuf/mbuf.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/mbuf/mbuf.h new file mode 100644 index 0000000..884c650 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/mbuf/mbuf.h @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef MBUF_H__ +#define MBUF_H__ + +/** + * @file mbuf.h + * + * @defgroup iot_socket_mbuf Memory management for socket + * @ingroup iot_sdk_socket + * @{ + * @brief Memory management for socket. + */ + +#include <stdbool.h> +#include "nrf_fifo.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**@brief Function for reading data from a buffer. */ +typedef uint32_t (*mbuf_read_fn)(void * p_ctx, + uint32_t read_offset, + uint8_t * p_dest, + uint32_t dest_len); + +/**@brief Function for checking buffer length. */ +typedef uint32_t (*mbuf_buf_len_fn)(void * p_ctx); + +/**@brief Function for freeing a buffer. */ +typedef void (*mbuf_free_fn)(void * p_ctx); + +/**@brief Memory management structure. */ +typedef struct +{ + nrf_fifo_t fifo; /**< FIFO for storing data buffers. */ + mbuf_read_fn read; /**< Function for reading data from a buffer. */ + mbuf_buf_len_fn buf_len; /**< Function for checking buffer length. */ + mbuf_free_fn free; /**< Function for freeing a buffer. */ + uint32_t read_pos; /**< Read position in the currently processed buffer. */ + void * p_current; /**< Pointer to the currently processed buffer. */ +} mbuf_t; + +/** + * @brief Function for initializing the memory buffer manager. + * + * This function allocates resources for the mbuf FIFO and initializes the mbuf. + * + * @param[in, out] p_mbuf Pointer to the mbuf structure to initialize. + * @param[in] read_fn Function for reading data from a buffer. + * @param[in] buf_len_fn Function for checking buffer length. + * @param[in] free_fn Function for freeing a buffer. + * @param[in] nmemb Maximum number of data buffers. + * + * @return NRF_SUCCESS on success, otherwise error code is returned. + */ +uint32_t mbuf_init(mbuf_t * p_mbuf, + mbuf_read_fn read_fn, + mbuf_buf_len_fn buf_len_fn, + mbuf_free_fn free_fn, + uint32_t nmemb); + +/** + * @brief Function for deinitializing the memory buffer manager. + * + * This function releases any resources allocated for mbuf instance pointed by p_mbuf. + * + * @param[in, out] p_mbuf Pointer to the mbuf structure to deinitialize. + */ +void mbuf_deinit(mbuf_t * p_mbuf); + +/** + * @brief Function for putting a data buffer in the mbuf. + * + * @param[in, out] p_mbuf Pointer to the mbuf structure that shall store the buffer. + * @param[in] p_ctx Pointer to the data buffer to store. + * + * @return NRF_SUCCESS on success, otherwise error code is returned. + */ +uint32_t mbuf_write(mbuf_t * p_mbuf, void * p_ctx); + +/** + * @brief Function for reading data from the mbuf. + * + * @param[in, out] p_mbuf Pointer to the mbuf structure to read data from. + * @param[in] p_buf Pointer to the buffer where data shall be read from. + * @param[out] buf_size Size of the buffer pointed by p_buf. + * + * @return NRF_SUCCESS on success, otherwise error code is returned. + */ +uint32_t mbuf_read(mbuf_t * p_mbuf, void * p_buf, uint32_t buf_size); + +/** + * @brief Function for checking if the mbuf is empty. + * + * @param[in] p_mbuf Pointer to the mbuf structure that shall be checked. + * + * @return True if mbuf is empty, false otherwise. + */ +bool mbuf_empty(mbuf_t * p_mbuf); + +#ifdef __cplusplus +} +#endif + +/**@} */ + +#endif // MBUF_H__ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/portdb/portdb.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/portdb/portdb.c new file mode 100644 index 0000000..7bfbcc4 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/portdb/portdb.c @@ -0,0 +1,185 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "portdb.h" +#include <string.h> +#include "iot_common.h" +#include "iot_errors.h" +#include "mem_manager.h" +#include "nrf_error.h" + +#define IANA_EPHEMRAL_BEGIN 49152u /**< Minimum port number of the ephemeral port range. */ +#define IANA_EPHEMRAL_END 65535u /**< Maximum port number of the ephemeral port range. */ + +static uint16_t * m_portdb; /**< A pointer to the port database. */ +static uint32_t m_portdb_len; /**< Length of the port database. */ + + + +static __INLINE uint32_t db_size_get(void) +{ + return m_portdb_len * sizeof(uint16_t); +} + + +static __INLINE void db_reset(void) +{ + memset(&m_portdb[0], 0, db_size_get()); +} + + +uint32_t portdb_init(uint32_t max_ports) +{ + uint32_t err_code = NRF_SUCCESS; + m_portdb_len = max_ports; + m_portdb = nrf_malloc(db_size_get()); + if (m_portdb == NULL) + { + err_code = NRF_ERROR_NO_MEM; + } + else + { + db_reset(); + } + return err_code; +} + + +void portdb_deinit(void) +{ + nrf_free(m_portdb); + m_portdb = NULL; + m_portdb_len = 0; +} + + +void portdb_reset(void) +{ + db_reset(); +} + + +static inline uint32_t check_port_in_use(uint16_t port) +{ + uint32_t err_code = NRF_SUCCESS; + + for (uint32_t i = 0; i < m_portdb_len; i++) + { + if (m_portdb[i] == port) + { + err_code = SOCKET_PORT_IN_USE; + break; + } + } + return err_code; +} + +static inline uint32_t find_free_index(uint32_t * p_idx) +{ + uint32_t err_code = SOCKET_NO_AVAILABLE_PORTS; + + for (uint32_t i = 0; i < m_portdb_len; i++) + { + if (m_portdb[i] == 0) + { + *p_idx = i; + err_code = NRF_SUCCESS; + break; + } + } + return err_code; +} + +static uint32_t portdb_find_available_index(uint32_t * p_idx, uint16_t port) +{ + uint32_t err_code = SOCKET_NO_AVAILABLE_PORTS; + + err_code = check_port_in_use(port); + + if (err_code == NRF_SUCCESS) + { + err_code = find_free_index(p_idx); + } + return err_code; +} + +uint32_t portdb_register(uint16_t port) +{ + uint32_t idx = 0; + uint32_t err_code = portdb_find_available_index(&idx, port); + + if (err_code == NRF_SUCCESS) + { + m_portdb[idx] = port; + } + return err_code; +} + +uint32_t portdb_alloc(uint16_t * p_port) +{ + uint32_t err_code = SOCKET_NO_AVAILABLE_PORTS; + for (uint32_t i = IANA_EPHEMRAL_BEGIN; i <= IANA_EPHEMRAL_END; i++) + { + uint16_t port = (uint16_t)i; + err_code = portdb_register(port); + + if (err_code == NRF_SUCCESS) + { + *p_port = port; + break; + } + else if (err_code == SOCKET_NO_AVAILABLE_PORTS) + { + break; + } + } + return err_code; +} + +void portdb_free(uint16_t port) +{ + for (uint32_t i = 0; i < m_portdb_len; i++) + { + if (m_portdb[i] == port) + { + m_portdb[i] = 0; + break; + } + } +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/portdb/portdb.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/portdb/portdb.h new file mode 100644 index 0000000..94f0d38 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/libraries/portdb/portdb.h @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef PORTDB_H__ +#define PORTDB_H__ + +/** + * @file portdb.h + * + * @defgroup iot_socket_portdb Port Database + * @ingroup iot_sdk_socket + * @{ + * @brief Port database for sockets. + * + * The port database provides a functionality for registering, allocating, and freeing ports. + */ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Function for initializing the port database. + * + * This must be called before allocating and freeing ports. + * + * @param max_len The max length of the portdb. + * + * @retval NRF_SUCCESS If successfully initialized. + */ +uint32_t portdb_init(uint32_t max_len); + +/** + * @brief Function for deinitializing the portdb. + * + * This will free all memory allocated by this portdb. + */ +void portdb_deinit(void); + +/** + * @brief Function to reset all ports without freeing any memories. + */ +void portdb_reset(void); + +/** + * @brief Function for allocating a port. + * + * Looks for an available port in the database and allocates it to the caller. + * + * @param[out] p_port Pointer to a variable where the allocated port number should be stored. + * + * @retval NRF_SUCCESS If a free port was located and successfully allocated. + * @retval SOCKET_NO_AVAILABLE_PORTS If no available ports were found. + */ +uint32_t portdb_alloc(uint16_t * p_port); + +/** + * @brief Function for registering a port. + * + * Marks a given port in the database as being in use. + * + * @param[in] port The port to mark as in use. + * + * @retval NRF_SUCCESS If port was successfully marked as in use. + * @retval SOCKET_NO_AVAILABLE_PORTS If there was no slot in which to register the port. + * @retval SOCKET_PORT_IN_USE If the port has already been registered or allocated. + */ +uint32_t portdb_register(uint16_t port); + +/** + * @brief Function for freeing a port. + * + * Mark a given port as free and make it available for others to register or allocate. + * + * @param[in] port The port to mark as free. + */ +void portdb_free(uint16_t port); + +#ifdef __cplusplus +} +#endif + +/**@} */ + +#endif // PORTDB_H__ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/platform/ble/socket_ble.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/platform/ble/socket_ble.c new file mode 100644 index 0000000..da2cc7b --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/platform/ble/socket_ble.c @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <stdint.h> +#include "nrf_soc.h" +#include "app_scheduler.h" + +uint32_t socket_wait(void) +{ + // Execute event schedule. + app_sched_execute(); + + return sd_app_evt_wait(); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/ipv6/transport_handler.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/ipv6/transport_handler.c new file mode 100644 index 0000000..a9d6edd --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/ipv6/transport_handler.c @@ -0,0 +1,387 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "iot_defines.h" +#include "iot_errors.h" +#include "sdk_config.h" +#include "socket_trace.h" +#include "socket_common.h" +#include "socket_config.h" +#include "config_medium.h" +#include "transport_if.h" +#include "udp_api.h" +#include "portdb.h" +#include "nrf_soc.h" +#include "nrf_fifo.h" + +/**@brief Verify that IPv6 address length is correct. */ +#define VERIFY_ADDRESS_LEN(len) \ + do { \ + if ((len) != sizeof(sockaddr_in6_t)) { \ + return NRF_ERROR_NULL | IOT_SOCKET_ERR_BASE; \ + } \ + } while (0) + +#define SOCKET_MAX_PENDING_PACKETS 32 /**< Maximum number of pending received packets. */ + +/**@brief Nordic IPv6 handle structure. */ +typedef struct +{ + udp6_socket_t socket; /**< UDP socket. */ + nrf_fifo_t recv_queue; /**< Received packets queue. */ + uint16_t local_port; /**< Local port number. */ +} ipv6_handle_t; + +static const char * unused = "UNUSED"; /**< A pointer indicating an unused socket. */ +static ipv6_handle_t ipv6_handles[SOCKET_MAX_SOCKET_COUNT]; /**< IPv6 handle array. */ + +void free_pbuffer(void * p_data) +{ + (void) iot_pbuffer_free((iot_pbuffer_t *)p_data, true); +} + +static ipv6_handle_t * ipv6_handle_allocate(socket_t * p_socket) +{ + ipv6_handle_t * p_handle = NULL; + for (uint32_t i = 0; i < SOCKET_MAX_SOCKET_COUNT; i++) + { + if (ipv6_handles[i].socket.p_app_data == unused) + { + p_handle = &ipv6_handles[i]; + p_handle->socket.p_app_data = p_socket; + (void) nrf_fifo_init(&p_handle->recv_queue, SOCKET_MAX_PENDING_PACKETS, socket_wait, free_pbuffer); + uint32_t err_code = udp6_socket_app_data_set(&p_handle->socket); + if (err_code != NRF_SUCCESS) + { + p_handle = NULL; + } + break; + } + } + return p_handle; +} + +static uint32_t ipv6_handle_free(ipv6_handle_t * p_handle) +{ + p_handle->local_port = 0; + p_handle->socket.p_app_data = (void *)unused; + return udp6_socket_app_data_set(&p_handle->socket); +} + +void transport_event_handler(iot_interface_t * p_interface, ipv6_event_t * p_event) +{ + (void) p_interface; + + switch (p_event->event_id) + { + case IPV6_EVT_INTERFACE_ADD: + transport_interface_up(); + break; + case IPV6_EVT_INTERFACE_DELETE: + transport_interface_down(); + break; + case IPV6_EVT_INTERFACE_RX_DATA: + break; + default: + break; + } +} + +void transport_handler_init(void) +{ + ipv6_init_t init_param; + init_param.p_eui64 = config_medium_local_iid(); + init_param.event_handler = transport_event_handler; + + uint32_t err_code = ipv6_init(&init_param); + (void) err_code; + // APP_ERROR_CHECK(err_code); + for (uint32_t i = 0; i< SOCKET_MAX_SOCKET_COUNT; i++) + { + ipv6_handles[i].local_port = 0; + ipv6_handles[i].socket.socket_id = 0; + ipv6_handles[i].socket.p_app_data = (void *)unused; + } +} + +static uint32_t ipv6_transport_open(socket_t * p_socket) +{ + uint32_t err_code = NRF_SUCCESS; + if (p_socket->so_params.so_type != SOCK_DGRAM) + { + err_code = SOCKET_UNSUPPORTED_PROTOCOL; + } + if (err_code == NRF_SUCCESS) + { + ipv6_handle_t * p_ipv6_handle = ipv6_handle_allocate(p_socket); + if (p_ipv6_handle == NULL) + { + err_code = NRF_ERROR_NULL; + } + else + { + err_code = udp6_socket_allocate(&p_ipv6_handle->socket); + if (err_code == NRF_SUCCESS) + { + p_socket->so_ctx = p_ipv6_handle; + } + else + { + (void) ipv6_handle_free(p_ipv6_handle); + } + } + } + return err_code; +} + +static uint32_t ipv6_transport_close(socket_t * p_socket) +{ + ipv6_handle_t * p_ipv6_handle = (ipv6_handle_t *) p_socket->so_ctx; + if (p_ipv6_handle->local_port > 0) + { + portdb_free(p_ipv6_handle->local_port); + } + + uint32_t err_code = udp6_socket_free(&p_ipv6_handle->socket); + if (err_code == NRF_SUCCESS) + { + err_code = ipv6_handle_free(p_ipv6_handle); + } + return err_code; +} + +static uint32_t ipv6_transport_recv_callback(const udp6_socket_t * p_udp_socket, + const ipv6_header_t * p_ip_header, + const udp6_header_t * p_udp_header, + uint32_t process_result, + iot_pbuffer_t * p_rx_packet) +{ + uint32_t err_code = process_result; + socket_t * p_socket = (socket_t *)p_udp_socket->p_app_data; + ipv6_handle_t * p_ipv6_handle = (ipv6_handle_t *)p_socket->so_ctx; + if (err_code == NRF_SUCCESS) + { + err_code = nrf_fifo_enq(&p_ipv6_handle->recv_queue, p_rx_packet, false); + if (err_code == NRF_SUCCESS) + { + p_socket->so_read_evt++; + err_code = IOT_IPV6_ERR_PENDING; + } + } + return err_code; +} + +static uint32_t ipv6_transport_bind(socket_t * p_socket, const void * p_addr, socklen_t addr_len) +{ + VERIFY_ADDRESS_LEN(addr_len); + const sockaddr_in6_t * p_addr_in6 = (const sockaddr_in6_t *)p_addr; + ipv6_handle_t * p_ipv6_handle = (ipv6_handle_t *)p_socket->so_ctx; + udp6_socket_t * p_udp_socket = &p_ipv6_handle->socket; + + p_ipv6_handle->local_port = HTONS(p_addr_in6->sin6_port); + uint32_t err_code = portdb_register(p_ipv6_handle->local_port); + if (err_code == NRF_SUCCESS) + { + ipv6_addr_t * p_ipv6_addr = (ipv6_addr_t *)&p_addr_in6->sin6_addr; + err_code = udp6_socket_bind(p_udp_socket, p_ipv6_addr, p_ipv6_handle->local_port); + } + if (err_code == NRF_SUCCESS) + { + err_code = udp6_socket_recv(p_udp_socket, ipv6_transport_recv_callback); + } + return err_code; +} + +static uint32_t ipv6_transport_bind_any(ipv6_handle_t * p_ipv6_handle) +{ + udp6_socket_t * p_udp_socket = &p_ipv6_handle->socket; + uint32_t err_code = portdb_alloc(&p_ipv6_handle->local_port); + if (err_code == NRF_SUCCESS) + { + SOCKET_TRACE("Binding to port %hu\r\n", p_ipv6_handle->local_port); + err_code = udp6_socket_bind(p_udp_socket, IPV6_ADDR_ANY, p_ipv6_handle->local_port); + } + + if (err_code == NRF_SUCCESS) + { + err_code = udp6_socket_recv(p_udp_socket, ipv6_transport_recv_callback); + } + return err_code; +} + +static uint32_t ipv6_transport_connect(socket_t * p_socket, const void * p_addr, socklen_t addr_len) +{ + VERIFY_ADDRESS_LEN(addr_len); + + ipv6_handle_t * p_ipv6_handle = (ipv6_handle_t *)p_socket->so_ctx; + udp6_socket_t * p_udp_socket = &p_ipv6_handle->socket; + const sockaddr_in6_t * p_addr_in6 = (const sockaddr_in6_t *)p_addr; + uint32_t err_code = NRF_SUCCESS; + + // Port might already have been bound with an explicit call to bind() + if (p_ipv6_handle->local_port == 0) + { + err_code = ipv6_transport_bind_any(p_ipv6_handle); + } + + if (err_code == NRF_SUCCESS) + { + // These data structures are compatible, and can therefore be cast + err_code = udp6_socket_connect(p_udp_socket, + (ipv6_addr_t *)&p_addr_in6->sin6_addr, + HTONS(p_addr_in6->sin6_port)); + } + + return err_code; +} + +static uint32_t ipv6_transport_send(socket_t * p_socket, + const void * p_buf, + uint32_t len, + int flags, + const void * p_destaddr, + socklen_t destaddr_len) +{ + ipv6_handle_t * p_ipv6_handle = (ipv6_handle_t *)p_socket->so_ctx; + udp6_socket_t * p_udp_socket = &p_ipv6_handle->socket; + uint32_t err_code = NRF_SUCCESS; + iot_pbuffer_alloc_param_t pbuff_param; + + // Ensure that port is bound before sending packet + if (p_ipv6_handle->local_port == 0) + { + err_code = ipv6_transport_bind_any(p_ipv6_handle); + } + + if (err_code == NRF_SUCCESS) + { + pbuff_param.flags = PBUFFER_FLAG_DEFAULT; + pbuff_param.type = UDP6_PACKET_TYPE; + pbuff_param.length = len; + + iot_pbuffer_t * p_buffer = NULL; + err_code = iot_pbuffer_allocate(&pbuff_param, &p_buffer); + if (err_code == NRF_SUCCESS) + { + memcpy(p_buffer->p_payload, p_buf, len); + if (p_destaddr != NULL && destaddr_len == sizeof(sockaddr_in6_t)) + { + sockaddr_in6_t * p_addr_in6 = (sockaddr_in6_t *)p_destaddr; + err_code = udp6_socket_sendto(p_udp_socket, + (ipv6_addr_t *)&p_addr_in6->sin6_addr, + HTONS(p_addr_in6->sin6_port), + p_buffer); + } + else + { + err_code = udp6_socket_send(p_udp_socket, p_buffer); + } + } + } + return err_code; +} + +static uint32_t ipv6_transport_recv(socket_t * p_socket, + void * p_buf, + uint32_t * p_sz, + int flags, + void * p_srcaddr, + socklen_t * p_srcaddr_len) +{ + if ((p_socket->so_flags & O_NONBLOCK) != 0 && + (flags & MSG_WAITALL) == 0) + { + flags |= MSG_DONTWAIT; + } + + ipv6_handle_t * p_ipv6_handle = (ipv6_handle_t *)p_socket->so_ctx; + iot_pbuffer_t * p_pbuffer = NULL; + uint32_t err_code = nrf_fifo_deq(&p_ipv6_handle->recv_queue, + (void **)&p_pbuffer, + (flags & MSG_DONTWAIT) == 0); + if (err_code == NRF_SUCCESS) + { + uint32_t copy_len = MIN(*p_sz, p_pbuffer->length); + memcpy(p_buf, p_pbuffer->p_payload, copy_len); + *p_sz = copy_len; + + if (p_srcaddr != NULL && p_srcaddr_len != NULL) + { + const udp6_header_t * p_udp_header = + (udp6_header_t *)(p_pbuffer->p_payload - UDP_HEADER_SIZE); + const ipv6_header_t * p_ipv6_header = + (ipv6_header_t *)(p_pbuffer->p_payload - UDP_HEADER_SIZE - IPV6_IP_HEADER_SIZE); + sockaddr_in6_t * p_srcsockaddr = (sockaddr_in6_t *)p_srcaddr; + + *p_srcaddr_len = sizeof(sockaddr_in6_t); + p_srcsockaddr->sin6_addr = *((in6_addr_t *)&p_ipv6_header->srcaddr); + p_srcsockaddr->sin6_port = HTONS(p_udp_header->srcport); + p_srcsockaddr->sin6_len = *p_srcaddr_len; + p_srcsockaddr->sin6_family = AF_INET6; + p_srcsockaddr->sin6_flowinfo = 0; + p_srcsockaddr->sin6_scope_id = 0; + } + + (void) iot_pbuffer_free(p_pbuffer, true); + p_socket->so_read_evt = 0; + } + return err_code; +} + +static uint32_t ipv6_transport_listen(socket_t * p_socket, int backlog) +{ + // Ignore as this does not make sense for UDP + (void) p_socket; + (void) backlog; + return NRF_SUCCESS; +} + +/** + * @brief Transport for Nordic IPv6 socket. + */ +socket_transport_t transport_impl = +{ + .open = ipv6_transport_open, + .bind = ipv6_transport_bind, + .connect = ipv6_transport_connect, + .send = ipv6_transport_send, + .recv = ipv6_transport_recv, + .listen = ipv6_transport_listen, + .close = ipv6_transport_close +}; diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/lwip/transport_handler.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/lwip/transport_handler.c new file mode 100644 index 0000000..4537053 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/socket/transport/lwip/transport_handler.c @@ -0,0 +1,630 @@ +/** + * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "iot_defines.h" +#include "iot_errors.h" +#include "sdk_config.h" +#include "socket_common.h" +#include "socket_trace.h" +#include "transport_if.h" +#include "portdb.h" +#include "app_timer.h" +#include "mem_manager.h" +#include "nrf_fifo.h" +#include "mbuf.h" +#include "app_util.h" +#include "nrf_platform_port.h" +#include "app_util_platform.h" + +#include "lwip/opt.h" +#include "lwip/init.h" +#include "lwip/tcp.h" +#include "lwip/timers.h" + +/**@brief Verify that IPv6 address length is correct. */ +#define VERIFY_ADDRESS_LEN(len) \ + do { \ + if ((len) != sizeof(sockaddr_in6_t)) { \ + return NRF_ERROR_NULL | IOT_SOCKET_ERR_BASE; \ + } \ + } while (0) + +#define LWIP_SYS_TIMER_INTERVAL APP_TIMER_TICKS(100) /**< Interval for timer used as trigger to send. */ + +#define SOCKET_MAX_PENDING_PACKETS 32 /**< Maximum number of pending received packets. */ +#define SOCKET_MAX_PENDING_CONNECTIONS 10 /**< Maximum number of simultaneous connections. */ + +/**@brief TCP state */ +typedef enum +{ + TCP_STATE_IDLE, + TCP_STATE_REQUEST_CONNECTION, + TCP_STATE_CONNECTED, + TCP_STATE_DATA_TX_IN_PROGRESS, + TCP_STATE_TCP_SEND_PENDING, + TCP_STATE_DISCONNECTED +} tcp_state_t; + +/**@brief LwIP handle structure. */ +typedef struct { + struct tcp_pcb * p_pcb; /**< A pointer to LwIP TCP protocol control block. */ + socket_t * p_socket; /**< A pointer to corresponding socket. */ + nrf_fifo_t conn_queue; /**< Connections queue. */ + mbuf_t mbuf; /**< Memory management queue. */ + volatile tcp_state_t tcp_state; /**< TCP state. */ +} lwip_handle_t; + +static lwip_handle_t lwip_handles[SOCKET_MAX_SOCKET_COUNT]; /**< LwIP handle array. */ + +/**@brief Timer for LwIP. */ +APP_TIMER_DEF(m_lwip_timer_id); + +static err_t lwip_recv_callback(void * p_arg, + struct tcp_pcb * p_pcb, + struct pbuf * p_pbuf, + err_t err); + +static uint32_t lwip_buf_read(void * p_ctx, + uint32_t read_offset, + uint8_t * p_destbuf, + uint32_t destbuf_len) +{ + struct pbuf * p_pbuf = (struct pbuf *)p_ctx; + uint32_t copy_len = MIN(destbuf_len, (p_pbuf->len - read_offset)); + memcpy(p_destbuf, (void *)(((uint8_t *)p_pbuf->payload) + read_offset), copy_len); + return copy_len; +} + +static uint32_t lwip_buf_len(void * p_ctx) +{ + struct pbuf * p_pbuf = (struct pbuf *)p_ctx; + return p_pbuf->len; +} + +static void lwip_buf_free(void * p_ctx) +{ + nrf_free(((struct pbuf *)p_ctx)->payload); + (void) pbuf_free((struct pbuf *)p_ctx); +} + +static void lwip_timer_callback(void * p_ctx) +{ + (void) p_ctx; + sys_check_timeouts(); +} + +void nrf_driver_interface_up(iot_interface_t const * p_interface) +{ + UNUSED_PARAMETER(p_interface); + transport_interface_up(); +} + +void nrf_driver_interface_down(iot_interface_t const * p_interface) +{ + UNUSED_PARAMETER(p_interface); + transport_interface_down(); +} + +void transport_handler_init(void) +{ + lwip_init(); + uint32_t err_code = nrf_driver_init(); + APP_ERROR_CHECK(err_code); + for (uint32_t i = 0; i < SOCKET_MAX_SOCKET_COUNT; i++) + { + lwip_handles[i].p_pcb = NULL; + lwip_handles[i].p_socket = NULL; + lwip_handles[i].tcp_state = TCP_STATE_IDLE; + } + err_code = app_timer_create(&m_lwip_timer_id, + APP_TIMER_MODE_REPEATED, + lwip_timer_callback); + APP_ERROR_CHECK(err_code); + err_code = app_timer_start(m_lwip_timer_id, LWIP_SYS_TIMER_INTERVAL, NULL); + APP_ERROR_CHECK(err_code); + SOCKET_TRACE("Initialized LWIP transport handler\r\n"); +} + +static void lwip_error_handler(void * p_arg, err_t err) +{ + (void) p_arg; + SOCKET_TRACE("Error occured: %d\r\n", (int)err); + if(err == ERR_ABRT) + { + portdb_reset(); + } +} + +static err_t lwip_poll_handler(void * p_arg, struct tcp_pcb * p_pcb) +{ + (void) p_arg; + (void) p_pcb; + + return ERR_OK; +} + +static void lwip_drop_connection(void * p_ctx) +{ + struct tcp_pcb * p_pcb = (struct tcp_pcb *)p_ctx; + tcp_abort(p_pcb); +} + +static lwip_handle_t * lwip_handle_allocate(socket_t * p_socket, struct tcp_pcb * p_pcb) +{ + lwip_handle_t * p_handle = NULL; + for (uint32_t i = 0; i < SOCKET_MAX_SOCKET_COUNT; i++) + { + if (lwip_handles[i].p_pcb == NULL) + { + p_handle = &lwip_handles[i]; + (void)mbuf_init(&p_handle->mbuf, + lwip_buf_read, + lwip_buf_len, + lwip_buf_free, + SOCKET_MAX_PENDING_PACKETS); + (void) nrf_fifo_init(&p_handle->conn_queue, + SOCKET_MAX_PENDING_CONNECTIONS, + socket_wait, + lwip_drop_connection); + p_handle->p_socket = p_socket; + p_handle->tcp_state = TCP_STATE_IDLE; + if (p_pcb == NULL) + { + p_handle->p_pcb = tcp_new(); + } + else + { + p_handle->p_pcb = p_pcb; + } + tcp_arg(p_handle->p_pcb, p_handle); + tcp_setprio(p_handle->p_pcb, TCP_PRIO_MIN); + tcp_recv(p_handle->p_pcb, lwip_recv_callback); + tcp_err(p_handle->p_pcb, lwip_error_handler); + tcp_poll(p_handle->p_pcb, lwip_poll_handler, 0); + break; + } + } + SOCKET_TRACE("Allocated LWIP socket handle\r\n"); + return p_handle; +} + +static void lwip_handle_free(lwip_handle_t * p_handle) +{ + nrf_fifo_deinit(&p_handle->conn_queue); + mbuf_deinit(&p_handle->mbuf); + p_handle->p_pcb = NULL; + p_handle->p_socket = NULL; + p_handle->tcp_state = TCP_STATE_IDLE; + SOCKET_TRACE("Released LWIP socket handle\r\n"); +} + +static uint32_t lwip_transport_open(socket_t * p_socket) +{ + lwip_handle_t * p_handle = NULL; + uint32_t err_code = NRF_SUCCESS; + switch (p_socket->so_params.so_type) + { + case SOCK_STREAM: + p_handle = lwip_handle_allocate(p_socket, NULL); + if (p_handle == NULL) + { + err_code = NRF_ERROR_NULL; + } + else + { + p_socket->so_ctx = p_handle; + } + break; + default: + err_code = NRF_ERROR_NULL; + break; + } + return err_code; +} + +uint32_t lwip_error_convert(err_t lwip_err) +{ + uint32_t err_code = NRF_ERROR_NULL; + switch (lwip_err) + { + case ERR_OK: + err_code = NRF_SUCCESS; + break; + case ERR_MEM: + err_code = SOCKET_NO_MEM; + break; + case ERR_TIMEOUT: + err_code = SOCKET_TIMEOUT; + break; + case ERR_RTE: + err_code = SOCKET_NO_ROUTE; + break; + default: + err_code = NRF_ERROR_NULL; + break; + } + return err_code; +} + +static uint32_t lwip_transport_close(socket_t * p_socket) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + portdb_free((uint16_t)p_handle->p_pcb->local_port); + err_t err_code = tcp_close(p_handle->p_pcb); + if (err_code == ERR_OK) + { + lwip_handle_free(p_handle); + } + return lwip_error_convert(err_code); +} + +static err_t lwip_connect_callback(void * p_arg, struct tcp_pcb * p_pcb, err_t err) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; + // TODO: Error check + SOCKET_TRACE("New connection\r\n"); + p_handle->tcp_state = TCP_STATE_CONNECTED; + return ERR_OK; +} + +static err_t lwip_recv_callback(void * p_arg, + struct tcp_pcb * p_pcb, + struct pbuf * p_pbuf, + err_t err) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; + socket_t * p_socket = p_handle->p_socket; + if (err == ERR_OK) + { + uint8_t * p_payload_cp = nrf_malloc(p_pbuf->len); + struct pbuf * p_pbuf_cp = pbuf_alloc(PBUF_RAW, p_pbuf->len, PBUF_REF); + + memcpy(p_payload_cp, p_pbuf->payload, p_pbuf->len); + p_pbuf_cp->payload = p_payload_cp; + p_pbuf_cp->len = p_pbuf->len; + p_pbuf_cp->tot_len = p_pbuf->tot_len; + + uint32_t err_code = mbuf_write(&p_handle->mbuf, p_pbuf_cp); + if (err_code == NRF_SUCCESS) + { + p_socket->so_read_evt++; + } + else + { + err = ERR_MEM; + } + } + return err; +} + +static uint32_t lwip_wait_for_state(lwip_handle_t * p_handle, tcp_state_t state) +{ + uint32_t err_code = NRF_SUCCESS; + while (err_code == NRF_SUCCESS && + p_handle->tcp_state != state && + p_handle->tcp_state != TCP_STATE_DISCONNECTED) + { + err_code = socket_wait(); + } + if (err_code == NRF_SUCCESS && p_handle->tcp_state != state) + { + err_code = NRF_ERROR_NULL; + } + return err_code; +} + +static void lwipaddr_to_sockaddr(const ip6_addr_t * p_lwip_addr, sockaddr_in6_t * p_addr_in6) +{ + struct in6_addr * addr = &p_addr_in6->sin6_addr; + addr->s6_addr[0] = p_lwip_addr->addr[0] & 0xFF; + addr->s6_addr[1] = (p_lwip_addr->addr[0] >> 8) & 0xFF; + addr->s6_addr[2] = (p_lwip_addr->addr[0] >> 16) & 0xFF; + addr->s6_addr[3] = (p_lwip_addr->addr[0] >> 24) & 0xFF; + + addr->s6_addr[4] = p_lwip_addr->addr[1] & 0xFF; + addr->s6_addr[5] = (p_lwip_addr->addr[1] >> 8) & 0xFF; + addr->s6_addr[6] = (p_lwip_addr->addr[1] >> 16) & 0xFF; + addr->s6_addr[7] = (p_lwip_addr->addr[1] >> 24) & 0xFF; + + addr->s6_addr[8] = p_lwip_addr->addr[2] & 0xFF; + addr->s6_addr[9] = (p_lwip_addr->addr[2] >> 8) & 0xFF; + addr->s6_addr[10] = (p_lwip_addr->addr[2] >> 16) & 0xFF; + addr->s6_addr[11] = (p_lwip_addr->addr[2] >> 24) & 0xFF; + + addr->s6_addr[12] = p_lwip_addr->addr[3] & 0xFF; + addr->s6_addr[13] = (p_lwip_addr->addr[3] >> 8) & 0xFF; + addr->s6_addr[14] = (p_lwip_addr->addr[3] >> 16) & 0xFF; + addr->s6_addr[15] = (p_lwip_addr->addr[3] >> 24) & 0xFF; +} + +static void sockaddr_to_lwipaddr(const sockaddr_in6_t * p_addr_in6, ip6_addr_t * p_lwip_addr) +{ + const struct in6_addr * addr = &p_addr_in6->sin6_addr; + IP6_ADDR_PART(p_lwip_addr, 0, addr->s6_addr[0], addr->s6_addr[1], addr->s6_addr[2], addr->s6_addr[3]); + IP6_ADDR_PART(p_lwip_addr, 1, addr->s6_addr[4], addr->s6_addr[5], addr->s6_addr[6], addr->s6_addr[7]); + IP6_ADDR_PART(p_lwip_addr, 2, addr->s6_addr[8], addr->s6_addr[9], addr->s6_addr[10], addr->s6_addr[11]); + IP6_ADDR_PART(p_lwip_addr, 3, addr->s6_addr[12], addr->s6_addr[13], addr->s6_addr[14], addr->s6_addr[15]); +} + +static uint32_t lwip_transport_bind(socket_t * p_socket, const void * p_addr, socklen_t addr_len) +{ + VERIFY_ADDRESS_LEN(addr_len); + const sockaddr_in6_t * p_addr_in6 = (const sockaddr_in6_t *)p_addr; + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + + uint16_t port = HTONS(p_addr_in6->sin6_port); + uint32_t err_code = portdb_register(port); + if (err_code == NRF_SUCCESS) + { + ip6_addr_t lwip_addr; + sockaddr_to_lwipaddr(p_addr_in6, &lwip_addr); + err_t err = tcp_bind(p_handle->p_pcb, &lwip_addr, port); + err_code = lwip_error_convert(err); + } + return err_code; +} + +static uint32_t lwip_transport_connect(socket_t * p_socket, + const void * p_addr, + socklen_t addr_len) +{ + VERIFY_ADDRESS_LEN(addr_len); + + bool is_blocking = ((p_socket->so_flags & O_NONBLOCK) == 0); + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + const sockaddr_in6_t * p_addr_in6 = (const sockaddr_in6_t *)p_addr; + uint16_t port = 0; + uint32_t err_code = portdb_alloc(&port); + + SOCKET_TRACE("Binding to port %d\r\n", (int)port); + if (err_code == NRF_SUCCESS) + { + ip6_addr_t any_addr; + ip6_addr_set_any(&any_addr); + err_t err = tcp_bind (p_handle->p_pcb, &any_addr, port); + SOCKET_TRACE("Err %d from bind\r\n", (int)err); + err_code = lwip_error_convert(err); + } + + if (err_code == NRF_SUCCESS) + { + ip6_addr_t remote_addr; + sockaddr_to_lwipaddr(p_addr_in6, &remote_addr); + + p_handle->tcp_state = TCP_STATE_REQUEST_CONNECTION; + err_t err = tcp_connect(p_handle->p_pcb, &remote_addr, HTONS(p_addr_in6->sin6_port), lwip_connect_callback); + SOCKET_TRACE("Err %d from connect\r\n", (int)err); + err_code = lwip_error_convert(err); + + if (err_code == NRF_SUCCESS && is_blocking) + { + err_code = lwip_wait_for_state(p_handle, TCP_STATE_CONNECTED); + } + } + if (err_code != NRF_SUCCESS) + { + SOCKET_TRACE("Error %d when connecting to socket\r\n", (int)err_code); + portdb_free(port); + } + else + { + SOCKET_TRACE("Successfully connected to remote host!\r\n"); + } + + return err_code; +} + +static err_t lwip_send_complete(void * p_arg, struct tcp_pcb * p_pcb, u16_t len) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; + if (p_handle->tcp_state == TCP_STATE_TCP_SEND_PENDING) + { + p_handle->tcp_state = TCP_STATE_DATA_TX_IN_PROGRESS; + } + return ERR_OK; +} + +static inline uint32_t lwip_check_connected(socket_t * p_socket) +{ + return (p_socket->so_state == STATE_CONNECTED || p_socket->so_params.so_type == SOCK_DGRAM) ? + NRF_SUCCESS : SOCKET_NOT_CONNECTED; +} + + +static uint32_t lwip_transport_send(socket_t * p_socket, + const void * p_buf, + uint32_t buf_len, + int flags, + const void * p_destaddr, + socklen_t destaddr_len) +{ + (void) p_destaddr; + (void) destaddr_len; + + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + if ((p_socket->so_flags & O_NONBLOCK) != 0 && + (flags & MSG_WAITALL) == 0) + { + flags |= MSG_DONTWAIT; + } + + uint32_t err_code = lwip_check_connected(p_socket); + if (err_code == NRF_SUCCESS) + { + uint32_t len = tcp_sndbuf(p_handle->p_pcb); + if (len >= buf_len) + { + tcp_sent(p_handle->p_pcb, lwip_send_complete); + p_handle->tcp_state = TCP_STATE_TCP_SEND_PENDING; + err_t err = tcp_write(p_handle->p_pcb, p_buf, buf_len, 1); + err_code = lwip_error_convert(err); + if (err_code == NRF_SUCCESS && + (flags & MSG_DONTWAIT) == 0) + { + err_code = lwip_wait_for_state(p_handle, TCP_STATE_DATA_TX_IN_PROGRESS); + } + } + else + { + err_code = SOCKET_NO_MEM; + } + } + return err_code; +} + + +static uint32_t lwip_transport_recv(socket_t * p_socket, + void * p_buf, + uint32_t * p_buf_size, + int flags, + void * p_srcaddr, + socklen_t * p_srcaddr_len) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + if ((p_socket->so_flags & O_NONBLOCK) != 0 && + (flags & MSG_WAITALL) == 0) + { + flags |= MSG_DONTWAIT; + } + uint32_t err_code = lwip_check_connected(p_socket); + if (err_code == NRF_SUCCESS) + { + if (mbuf_empty(&p_handle->mbuf) == true) + { + if ((flags & MSG_DONTWAIT) != 0) + { + err_code = SOCKET_WOULD_BLOCK; + } + else + { + while ((mbuf_empty(&p_handle->mbuf) == true) && (err_code == NRF_SUCCESS)) + { + err_code = socket_wait(); + } + } + } + } + + if (err_code == NRF_SUCCESS) + { + *p_buf_size = mbuf_read(&p_handle->mbuf, p_buf, *p_buf_size); + tcp_recved(p_handle->p_pcb, *p_buf_size); + p_socket->so_read_evt = 0; + } + return err_code; +} + +static err_t lwip_accept_callback(void * p_arg, struct tcp_pcb * p_pcb, err_t err) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; + socket_t * p_socket = p_handle->p_socket; + if (err == ERR_OK) + { + (void) nrf_fifo_enq(&p_handle->conn_queue, p_pcb, true); + // TODO: Error check + p_socket->so_read_evt++; + } + return err; +} + +static uint32_t lwip_transport_listen(socket_t * p_socket, int backlog) +{ + lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; + uint32_t err_code = NRF_SUCCESS; + struct tcp_pcb * p_pcb = tcp_listen_with_backlog(p_handle->p_pcb, backlog); + if (p_pcb == NULL) + { + err_code = SOCKET_ADDRESS_IN_USE; + } + else + { + p_handle->p_pcb = p_pcb; + tcp_accept(p_handle->p_pcb, lwip_accept_callback); + } + return err_code; +} + +static uint32_t lwip_transport_accept(socket_t * p_socket, + socket_t * p_client, + void * p_cliaddr, + socklen_t * p_cliaddr_len) +{ + VERIFY_ADDRESS_LEN(*p_cliaddr_len); + struct tcp_pcb * p_client_pcb = NULL; + lwip_handle_t * p_lwip_handle = (lwip_handle_t *)p_socket->so_ctx; + uint32_t err_code = nrf_fifo_deq(&p_lwip_handle->conn_queue, + (void **)&p_client_pcb, + (p_socket->so_flags & O_NONBLOCK) == 0); + + if (err_code == NRF_SUCCESS) + { + lwip_handle_t * p_lwip_client = lwip_handle_allocate(p_client, p_client_pcb); + sockaddr_in6_t * p_sockaddr = (sockaddr_in6_t *)p_cliaddr; + + p_client->so_ctx = p_lwip_client; + + lwipaddr_to_sockaddr(&p_client_pcb->remote_ip, p_sockaddr); + p_sockaddr->sin6_port = p_client_pcb->remote_port; + p_sockaddr->sin6_len = *p_cliaddr_len; + p_sockaddr->sin6_family = AF_INET6; + p_sockaddr->sin6_flowinfo = 0; + p_sockaddr->sin6_scope_id = 0; + + /*lint -save -e548 */ + tcp_accepted(p_lwip_handle->p_pcb); + /*lint -restore */ + } + + return err_code; +} + +/** + * @brief Transport for LwIP socket. + */ +socket_transport_t transport_impl = +{ + .open = lwip_transport_open, + .bind = lwip_transport_bind, + .connect = lwip_transport_connect, + .send = lwip_transport_send, + .recv = lwip_transport_recv, + .listen = lwip_transport_listen, + .accept = lwip_transport_accept, + .close = lwip_transport_close +}; |