spider-bot/fw/nrf52/nrf5_sdk/components/iot/coap/coap.c

855 lines
26 KiB
C

/**
* Copyright (c) 2014 - 2019, 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();
}