855 lines
26 KiB
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();
|
|
}
|