From 3061ecca3d0fdfb87dabbf5f63c9e06c2a30f53a Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Thu, 23 Aug 2018 17:08:59 +0200 Subject: o Initial import. --- .../components/iot/mqtt/mqtt.c | 821 +++++++++++++++++++++ .../components/iot/mqtt/mqtt.h | 506 +++++++++++++ .../components/iot/mqtt/mqtt_decoder.c | 262 +++++++ .../components/iot/mqtt/mqtt_encoder.c | 457 ++++++++++++ .../components/iot/mqtt/mqtt_internal.h | 446 +++++++++++ .../components/iot/mqtt/mqtt_rx.c | 313 ++++++++ .../components/iot/mqtt/mqtt_rx.h | 74 ++ .../components/iot/mqtt/mqtt_transport.c | 88 +++ .../components/iot/mqtt/mqtt_transport.h | 233 ++++++ .../components/iot/mqtt/mqtt_transport_lwip.c | 349 +++++++++ .../components/iot/mqtt/mqtt_transport_socket.c | 319 ++++++++ .../components/iot/mqtt/mqtt_transport_tls.c | 185 +++++ 12 files changed, 4053 insertions(+) create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_decoder.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_encoder.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_internal.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_rx.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_rx.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport.h create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_lwip.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_socket.c create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt/mqtt_transport_tls.c (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/mqtt') 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 +#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 +#include +#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 +#include +#include + +#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); +} -- cgit v1.2.3