ec3ec8cd2a
Add macro that allows registration of initialization functions that are called when LwM2M engine starts. On LwM2M engine starts up, it first executes all initialization functions in following priority order: 1. LWM2M_PRIO_ENGINE 2. LWM2M_PRIO_CORE, this is where all LwM2M core objects are initialized 3. LWM2M_PRIO_OBJ, this is where all other objects are initialized 4. LwM2M_PRIO_APP, application initialization. Now on the initialization phase, we could rely that certain objects have already been registered. For example custom objects can register callbacks to core objects. On application phase, we can initialize sensor objects and register their callbacks because objects have already been initialized. This LWM2M_ON_INIT() should replace all use of SYS_INIT() with the default CONFIG_KERNEL_INIT_PRIORITY_DEFAULT. Priority order is actually just alphabetical order of names, so the order is set on a linkin phase, and we don't need any runtime checking for it. Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
1795 lines
45 KiB
C
1795 lines
45 KiB
C
/*
|
|
* Copyright (c) 2017 Linaro Limited
|
|
* Copyright (c) 2017-2019 Foundries.io
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2015, Yanzi Networks AB.
|
|
* 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 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 the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDER 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.
|
|
*/
|
|
|
|
/*
|
|
* Original authors:
|
|
* Joakim Eriksson <joakime@sics.se>
|
|
* Niclas Finne <nfi@sics.se>
|
|
* Joel Hoglund <joel@sics.se>
|
|
*/
|
|
|
|
#define LOG_MODULE_NAME net_lwm2m_rd_client
|
|
#define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
|
|
|
#include <zephyr/types.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/sys/printk.h>
|
|
#include <zephyr/net/socket.h>
|
|
|
|
#include "lwm2m_object.h"
|
|
#include "lwm2m_engine.h"
|
|
#include "lwm2m_rd_client.h"
|
|
#include "lwm2m_rw_link_format.h"
|
|
#include "lwm2m_util.h"
|
|
#include "lwm2m_obj_server.h"
|
|
|
|
#define LWM2M_RD_CLIENT_URI "rd"
|
|
#define CLIENT_EP_LEN CONFIG_LWM2M_RD_CLIENT_ENDPOINT_NAME_MAX_LENGTH
|
|
#define CLIENT_BINDING_LEN sizeof("UQ")
|
|
#define CLIENT_QUEUE_LEN sizeof("Q")
|
|
#define DELAY_BEFORE_CLOSING (1 * MSEC_PER_SEC)
|
|
#define DELAY_FOR_ACK 100U
|
|
#define EXCHANGE_LIFETIME 247U
|
|
#define MINIMUM_PERIOD 15
|
|
#define DISABLE_TIMEOUT (K_SECONDS(CONFIG_LWM2M_RD_CLIENT_MAX_RETRIES * EXCHANGE_LIFETIME))
|
|
|
|
static void sm_handle_registration_update_failure(void);
|
|
static int sm_send_registration_msg(void);
|
|
static bool sm_is_suspended(void);
|
|
static void lwm2m_rd_client_service(struct k_work *work);
|
|
static int64_t calc_next_event(void);
|
|
static void set_sm_state_delayed(uint8_t sm_state, int64_t delay_ms);
|
|
static void set_sm_state(uint8_t sm_state);
|
|
/** Try to fallback to bootstrap. Return true if we did. */
|
|
static bool fallback_to_bootstrap(void);
|
|
|
|
/* The states for the RD client state machine */
|
|
/*
|
|
* When node is unregistered it ends up in UNREGISTERED
|
|
* and this is going to be there until use X or Y kicks it
|
|
* back into INIT again
|
|
*/
|
|
enum sm_engine_state {
|
|
ENGINE_IDLE,
|
|
ENGINE_INIT,
|
|
ENGINE_DO_BOOTSTRAP_REG,
|
|
ENGINE_BOOTSTRAP_REG_SENT,
|
|
ENGINE_BOOTSTRAP_REG_DONE,
|
|
ENGINE_BOOTSTRAP_TRANS_DONE,
|
|
ENGINE_DO_REGISTRATION,
|
|
ENGINE_SEND_REGISTRATION,
|
|
ENGINE_REGISTRATION_SENT,
|
|
ENGINE_REGISTRATION_DONE,
|
|
ENGINE_REGISTRATION_DONE_RX_OFF,
|
|
ENGINE_UPDATE_REGISTRATION,
|
|
ENGINE_UPDATE_SENT,
|
|
ENGINE_SERVER_DISABLED,
|
|
ENGINE_SUSPENDED,
|
|
ENGINE_DEREGISTER,
|
|
ENGINE_DEREGISTER_SENT,
|
|
ENGINE_DEREGISTERED,
|
|
ENGINE_NETWORK_ERROR,
|
|
};
|
|
|
|
struct lwm2m_rd_client_info {
|
|
struct k_mutex mutex;
|
|
struct lwm2m_message rd_message;
|
|
struct lwm2m_ctx *ctx;
|
|
uint32_t lifetime;
|
|
uint8_t engine_state;
|
|
uint8_t retries;
|
|
uint8_t retry_delay;
|
|
|
|
int64_t last_update;
|
|
int64_t last_tx;
|
|
int64_t next_event;
|
|
int64_t last_state_change;
|
|
|
|
char ep_name[CLIENT_EP_LEN];
|
|
char server_ep[CLIENT_EP_LEN];
|
|
|
|
bool use_bootstrap : 1;
|
|
bool trigger_update : 1;
|
|
bool update_objects : 1;
|
|
bool close_socket : 1;
|
|
bool server_disabled: 1;
|
|
} client;
|
|
|
|
/* Allocate some data for queries and updates. Make sure it's large enough to
|
|
* hold the largest query string, which in most cases will be the endpoint
|
|
* string. In other case, 32 bytes are enough to encode any other query string
|
|
* documented in the LwM2M specification.
|
|
*/
|
|
static char query_buffer[MAX(32, sizeof("ep=") + CLIENT_EP_LEN)];
|
|
static enum sm_engine_state suspended_client_state;
|
|
|
|
static struct lwm2m_message *rd_get_message(void)
|
|
{
|
|
if (client.rd_message.ctx) {
|
|
/* Free old message */
|
|
lwm2m_reset_message(&client.rd_message, true);
|
|
}
|
|
|
|
client.rd_message.ctx = client.ctx;
|
|
return &client.rd_message;
|
|
|
|
}
|
|
|
|
static void rd_client_message_free(void)
|
|
{
|
|
lwm2m_reset_message(lwm2m_get_ongoing_rd_msg(), true);
|
|
}
|
|
|
|
|
|
struct lwm2m_message *lwm2m_get_ongoing_rd_msg(void)
|
|
{
|
|
if (!client.ctx || !client.rd_message.ctx) {
|
|
return NULL;
|
|
}
|
|
return &client.rd_message;
|
|
}
|
|
|
|
void engine_update_tx_time(void)
|
|
{
|
|
client.last_tx = k_uptime_get();
|
|
}
|
|
|
|
static void next_event_at(int64_t timestamp)
|
|
{
|
|
client.next_event = timestamp;
|
|
(void)lwm2m_engine_call_at(lwm2m_rd_client_service, timestamp);
|
|
}
|
|
|
|
static void set_sm_state_delayed(uint8_t sm_state, int64_t delay_ms)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
|
|
|
|
/* Determine if a callback to the app is needed */
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
if (sm_state == ENGINE_BOOTSTRAP_REG_DONE) {
|
|
event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_COMPLETE;
|
|
} else if (client.engine_state == ENGINE_BOOTSTRAP_TRANS_DONE &&
|
|
sm_state == ENGINE_DO_REGISTRATION) {
|
|
event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_TRANSFER_COMPLETE;
|
|
} else
|
|
#endif
|
|
if (client.engine_state == ENGINE_UPDATE_SENT &&
|
|
(sm_state == ENGINE_REGISTRATION_DONE ||
|
|
sm_state == ENGINE_REGISTRATION_DONE_RX_OFF)) {
|
|
lwm2m_push_queued_buffers(client.ctx);
|
|
event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE_COMPLETE;
|
|
} else if (sm_state == ENGINE_REGISTRATION_DONE) {
|
|
lwm2m_push_queued_buffers(client.ctx);
|
|
event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE;
|
|
} else if (sm_state == ENGINE_REGISTRATION_DONE_RX_OFF) {
|
|
event = LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF;
|
|
} else if (sm_state == ENGINE_DEREGISTERED &&
|
|
(client.engine_state >= ENGINE_DO_REGISTRATION &&
|
|
client.engine_state <= ENGINE_DEREGISTER_SENT) && !client.server_disabled) {
|
|
event = LWM2M_RD_CLIENT_EVENT_DISCONNECT;
|
|
} else if (sm_state == ENGINE_UPDATE_REGISTRATION) {
|
|
event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE;
|
|
} else if (sm_state == ENGINE_DEREGISTER) {
|
|
if (client.server_disabled) {
|
|
event = LWM2M_RD_CLIENT_EVENT_SERVER_DISABLED;
|
|
} else {
|
|
event = LWM2M_RD_CLIENT_EVENT_DEREGISTER;
|
|
}
|
|
}
|
|
|
|
if (sm_is_suspended()) {
|
|
/* Just change the state where we are going to resume next */
|
|
suspended_client_state = sm_state;
|
|
} else {
|
|
client.engine_state = sm_state;
|
|
}
|
|
|
|
if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.ctx->event_cb) {
|
|
client.ctx->event_cb(client.ctx, event);
|
|
}
|
|
|
|
/* Suspend socket after Event callback */
|
|
if (event == LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF) {
|
|
if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUSPEND_SOCKET_AT_IDLE) ||
|
|
IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_STOP_POLLING_AT_IDLE)) {
|
|
lwm2m_socket_suspend(client.ctx);
|
|
} else if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_CLOSE_SOCKET_AT_IDLE)) {
|
|
lwm2m_close_socket(client.ctx);
|
|
}
|
|
}
|
|
client.last_state_change = k_uptime_get();
|
|
next_event_at(k_uptime_get() + delay_ms);
|
|
k_mutex_unlock(&client.mutex);
|
|
}
|
|
|
|
static void set_sm_state(uint8_t sm_state)
|
|
{
|
|
set_sm_state_delayed(sm_state, 0);
|
|
}
|
|
|
|
static bool sm_is_bootstrap(void)
|
|
{
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
bool is_bootstrap = (client.engine_state >= ENGINE_DO_BOOTSTRAP_REG &&
|
|
client.engine_state <= ENGINE_BOOTSTRAP_TRANS_DONE);
|
|
k_mutex_unlock(&client.mutex);
|
|
return is_bootstrap;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
static bool sm_is_registered(void)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
bool registered = (client.engine_state >= ENGINE_REGISTRATION_DONE &&
|
|
client.engine_state <= ENGINE_DEREGISTER_SENT);
|
|
|
|
k_mutex_unlock(&client.mutex);
|
|
return registered;
|
|
}
|
|
|
|
static bool sm_is_suspended(void)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
bool suspended = (client.engine_state == ENGINE_SUSPENDED);
|
|
|
|
k_mutex_unlock(&client.mutex);
|
|
return suspended;
|
|
}
|
|
|
|
|
|
static uint8_t get_sm_state(void)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
uint8_t state = client.engine_state;
|
|
|
|
k_mutex_unlock(&client.mutex);
|
|
return state;
|
|
}
|
|
|
|
static void sm_handle_timeout_state(enum sm_engine_state sm_state)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
|
|
|
|
/* Don't send BOOTSTRAP_REG_FAILURE event, that is only emitted from
|
|
* do_network_error() once we are out of retries.
|
|
*/
|
|
if (client.engine_state == ENGINE_REGISTRATION_SENT) {
|
|
event = LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT;
|
|
} else if (client.engine_state == ENGINE_UPDATE_SENT) {
|
|
event = LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT;
|
|
} else if (client.engine_state == ENGINE_DEREGISTER_SENT) {
|
|
event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE;
|
|
} else {
|
|
/* TODO: unknown timeout state */
|
|
}
|
|
|
|
set_sm_state(sm_state);
|
|
|
|
if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.ctx->event_cb) {
|
|
client.ctx->event_cb(client.ctx, event);
|
|
}
|
|
k_mutex_unlock(&client.mutex);
|
|
}
|
|
|
|
static void sm_handle_failure_state(enum sm_engine_state sm_state)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
|
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
if (client.engine_state == ENGINE_BOOTSTRAP_REG_SENT) {
|
|
event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE;
|
|
} else
|
|
#endif
|
|
if (client.engine_state == ENGINE_REGISTRATION_SENT) {
|
|
event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE;
|
|
} else if (client.engine_state == ENGINE_UPDATE_SENT) {
|
|
sm_handle_registration_update_failure();
|
|
k_mutex_unlock(&client.mutex);
|
|
return;
|
|
} else if (client.engine_state == ENGINE_DEREGISTER_SENT) {
|
|
event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE;
|
|
}
|
|
|
|
lwm2m_engine_stop(client.ctx);
|
|
set_sm_state(sm_state);
|
|
|
|
if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.ctx->event_cb) {
|
|
client.ctx->event_cb(client.ctx, event);
|
|
}
|
|
k_mutex_unlock(&client.mutex);
|
|
}
|
|
|
|
/* force state machine restart */
|
|
static void socket_fault_cb(int error)
|
|
{
|
|
LOG_ERR("RD Client socket error: %d", error);
|
|
lwm2m_socket_close(client.ctx);
|
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) && sm_is_bootstrap()) {
|
|
client.ctx->sec_obj_inst = -1;
|
|
/* force full registration */
|
|
client.last_update = 0;
|
|
|
|
if (get_sm_state() == ENGINE_BOOTSTRAP_TRANS_DONE) {
|
|
/* Ignore the error, some servers close the connection immediately
|
|
* after receiving Ack to Bootstrap-Finish command.
|
|
*/
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Network error state causes engine to re-register,
|
|
* so only trigger that state if we are not stopping the
|
|
* engine.
|
|
* Also when engine is going to be disabled, for a while, we might get spurious
|
|
* socket errors when closing, so ignore them.
|
|
*/
|
|
if (client.engine_state > ENGINE_IDLE &&
|
|
client.engine_state < ENGINE_SERVER_DISABLED) {
|
|
sm_handle_timeout_state(ENGINE_NETWORK_ERROR);
|
|
} else if (client.engine_state != ENGINE_SUSPENDED &&
|
|
!client.server_disabled) {
|
|
sm_handle_failure_state(ENGINE_IDLE);
|
|
}
|
|
}
|
|
|
|
/* force re-update with remote peer */
|
|
void engine_trigger_update(bool update_objects)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
if (client.engine_state < ENGINE_REGISTRATION_SENT ||
|
|
client.engine_state > ENGINE_UPDATE_SENT) {
|
|
k_mutex_unlock(&client.mutex);
|
|
return;
|
|
}
|
|
|
|
client.trigger_update = true;
|
|
/* short delay for Ack, then trigger an update */
|
|
next_event_at(k_uptime_get() + DELAY_FOR_ACK);
|
|
|
|
if (update_objects) {
|
|
client.update_objects = true;
|
|
}
|
|
k_mutex_unlock(&client.mutex);
|
|
}
|
|
|
|
static inline const char *code2str(uint8_t code)
|
|
{
|
|
switch (code) {
|
|
case COAP_RESPONSE_CODE_BAD_REQUEST:
|
|
return "Bad Request";
|
|
case COAP_RESPONSE_CODE_FORBIDDEN:
|
|
return "Forbidden";
|
|
case COAP_RESPONSE_CODE_NOT_FOUND:
|
|
return "Not Found";
|
|
case COAP_RESPONSE_CODE_PRECONDITION_FAILED:
|
|
return "Precondition Failed";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
/* state machine reply callbacks */
|
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
static int do_bootstrap_reply_cb(const struct coap_packet *response,
|
|
struct coap_reply *reply,
|
|
const struct sockaddr *from)
|
|
{
|
|
uint8_t code;
|
|
|
|
code = coap_header_get_code(response);
|
|
LOG_DBG("Bootstrap callback (code:%u.%u)",
|
|
COAP_RESPONSE_CODE_CLASS(code),
|
|
COAP_RESPONSE_CODE_DETAIL(code));
|
|
|
|
if (code == COAP_RESPONSE_CODE_CHANGED) {
|
|
LOG_INF("Bootstrap registration done!");
|
|
set_sm_state(ENGINE_BOOTSTRAP_REG_DONE);
|
|
return 0;
|
|
}
|
|
|
|
LOG_ERR("Failed with code %u.%u (%s). Not Retrying.",
|
|
COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
|
|
code2str(code));
|
|
|
|
sm_handle_failure_state(ENGINE_IDLE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void do_bootstrap_reg_timeout_cb(struct lwm2m_message *msg)
|
|
{
|
|
LOG_WRN("Bootstrap Timeout");
|
|
sm_handle_timeout_state(ENGINE_NETWORK_ERROR);
|
|
}
|
|
#endif
|
|
|
|
int engine_trigger_bootstrap(void)
|
|
{
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
|
|
if (client.use_bootstrap) {
|
|
/* Bootstrap is not possible to trig */
|
|
LOG_WRN("Bootstrap process ongoing");
|
|
k_mutex_unlock(&client.mutex);
|
|
return -EPERM;
|
|
}
|
|
LOG_INF("Server Initiated Bootstrap");
|
|
/* Free ongoing possible message */
|
|
rd_client_message_free();
|
|
client.use_bootstrap = true;
|
|
client.trigger_update = false;
|
|
set_sm_state_delayed(ENGINE_INIT, DELAY_BEFORE_CLOSING);
|
|
k_mutex_unlock(&client.mutex);
|
|
return 0;
|
|
#else
|
|
return -EPERM;
|
|
#endif
|
|
}
|
|
static int do_registration_reply_cb(const struct coap_packet *response,
|
|
struct coap_reply *reply,
|
|
const struct sockaddr *from)
|
|
{
|
|
struct coap_option options[2];
|
|
uint8_t code;
|
|
int ret = -EINVAL;
|
|
|
|
code = coap_header_get_code(response);
|
|
LOG_DBG("Registration callback (code:%u.%u)",
|
|
COAP_RESPONSE_CODE_CLASS(code),
|
|
COAP_RESPONSE_CODE_DETAIL(code));
|
|
|
|
/* check state and possibly set registration to done */
|
|
if (code == COAP_RESPONSE_CODE_CREATED) {
|
|
ret = coap_find_options(response, COAP_OPTION_LOCATION_PATH,
|
|
options, 2);
|
|
if (ret < 2) {
|
|
LOG_ERR("Unexpected endpoint data returned. ret = %d", ret);
|
|
ret = -EINVAL;
|
|
goto fail;
|
|
}
|
|
|
|
/* option[0] should be "rd" */
|
|
|
|
if (options[1].len + 1 > sizeof(client.server_ep)) {
|
|
LOG_ERR("Unexpected length of query: "
|
|
"%u (expected %zu)\n",
|
|
options[1].len,
|
|
sizeof(client.server_ep));
|
|
ret = -EINVAL;
|
|
goto fail;
|
|
}
|
|
|
|
/* remember the last reg time */
|
|
client.last_update = k_uptime_get();
|
|
client.server_disabled = false;
|
|
client.retries = 0;
|
|
|
|
memcpy(client.server_ep, options[1].value,
|
|
options[1].len);
|
|
client.server_ep[options[1].len] = '\0';
|
|
set_sm_state(ENGINE_REGISTRATION_DONE);
|
|
LOG_INF("Registration Done (EP='%s')",
|
|
client.server_ep);
|
|
|
|
return 0;
|
|
}
|
|
|
|
LOG_ERR("Failed with code %u.%u (%s).",
|
|
COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
|
|
code2str(code));
|
|
fail:
|
|
lwm2m_server_disable(client.ctx->srv_obj_inst, DISABLE_TIMEOUT);
|
|
sm_handle_failure_state(ENGINE_NETWORK_ERROR);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void do_registration_timeout_cb(struct lwm2m_message *msg)
|
|
{
|
|
LOG_WRN("Registration Timeout");
|
|
|
|
sm_handle_timeout_state(ENGINE_NETWORK_ERROR);
|
|
}
|
|
|
|
static int do_update_reply_cb(const struct coap_packet *response,
|
|
struct coap_reply *reply,
|
|
const struct sockaddr *from)
|
|
{
|
|
uint8_t code;
|
|
|
|
code = coap_header_get_code(response);
|
|
LOG_INF("Update callback (code:%u.%u)",
|
|
COAP_RESPONSE_CODE_CLASS(code),
|
|
COAP_RESPONSE_CODE_DETAIL(code));
|
|
|
|
/* If NOT_FOUND just continue on */
|
|
if ((code == COAP_RESPONSE_CODE_CHANGED) ||
|
|
(code == COAP_RESPONSE_CODE_CREATED)) {
|
|
/* remember the last reg time */
|
|
client.last_update = k_uptime_get();
|
|
set_sm_state(ENGINE_REGISTRATION_DONE);
|
|
LOG_INF("Update Done");
|
|
return 0;
|
|
}
|
|
|
|
LOG_ERR("Failed with code %u.%u (%s). Retrying registration.",
|
|
COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
|
|
code2str(code));
|
|
|
|
sm_handle_failure_state(ENGINE_DO_REGISTRATION);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void do_update_timeout_cb(struct lwm2m_message *msg)
|
|
{
|
|
LOG_WRN("Registration Update Timeout");
|
|
|
|
if (client.ctx->sock_fd > -1) {
|
|
client.close_socket = true;
|
|
}
|
|
/* Re-do registration */
|
|
sm_handle_timeout_state(ENGINE_DO_REGISTRATION);
|
|
}
|
|
|
|
static int do_deregister_reply_cb(const struct coap_packet *response,
|
|
struct coap_reply *reply,
|
|
const struct sockaddr *from)
|
|
{
|
|
uint8_t code;
|
|
|
|
code = coap_header_get_code(response);
|
|
LOG_DBG("Deregister callback (code:%u.%u)",
|
|
COAP_RESPONSE_CODE_CLASS(code),
|
|
COAP_RESPONSE_CODE_DETAIL(code));
|
|
|
|
if (code == COAP_RESPONSE_CODE_DELETED) {
|
|
LOG_INF("Deregistration success");
|
|
set_sm_state(ENGINE_DEREGISTERED);
|
|
return 0;
|
|
}
|
|
|
|
LOG_ERR("Failed with code %u.%u (%s). Not Retrying",
|
|
COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
|
|
code2str(code));
|
|
|
|
sm_handle_failure_state(ENGINE_DEREGISTERED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void do_deregister_timeout_cb(struct lwm2m_message *msg)
|
|
{
|
|
LOG_WRN("De-Registration Timeout");
|
|
|
|
sm_handle_timeout_state(ENGINE_DEREGISTERED);
|
|
}
|
|
|
|
static bool is_bootsrap_server(int sec_obj_inst)
|
|
{
|
|
bool bootstrap;
|
|
int ret;
|
|
|
|
ret = lwm2m_get_bool(&LWM2M_OBJ(0, sec_obj_inst, 1), &bootstrap);
|
|
if (ret < 0) {
|
|
LOG_WRN("Failed to check bootstrap, err %d", ret);
|
|
return false;
|
|
}
|
|
return bootstrap;
|
|
}
|
|
|
|
static bool sm_update_lifetime(int srv_obj_inst, uint32_t *lifetime)
|
|
{
|
|
uint32_t new_lifetime;
|
|
|
|
if (lwm2m_get_u32(&LWM2M_OBJ(1, srv_obj_inst, 1), &new_lifetime) < 0) {
|
|
new_lifetime = CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME;
|
|
LOG_INF("Using default lifetime: %u", new_lifetime);
|
|
}
|
|
|
|
if (new_lifetime < CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME) {
|
|
new_lifetime = CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME;
|
|
lwm2m_set_u32(&LWM2M_OBJ(1, srv_obj_inst, 1), new_lifetime);
|
|
LOG_INF("Overwrite a server lifetime with default");
|
|
}
|
|
|
|
if (new_lifetime != *lifetime) {
|
|
*lifetime = new_lifetime;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Find the next security instance for bootstrapping.
|
|
*
|
|
* Search for the next security instance that has the bootstrap flag set and
|
|
* is not the same as current security instance.
|
|
*
|
|
* @param sec_obj_inst current security instance or -1.
|
|
* @return zero on success, negative on error.
|
|
*/
|
|
static int sm_next_bootstrap_inst(int *sec_obj_inst)
|
|
{
|
|
int i, obj_inst_id = -1;
|
|
|
|
if (*sec_obj_inst >= 0 && !is_bootsrap_server(*sec_obj_inst)) {
|
|
*sec_obj_inst = -1;
|
|
}
|
|
|
|
/* Iterate over all instances to find the correct one. */
|
|
for (i = 0; i < CONFIG_LWM2M_SECURITY_INSTANCE_COUNT; i++) {
|
|
obj_inst_id = lwm2m_security_index_to_inst_id(i);
|
|
if (obj_inst_id < 0) {
|
|
continue;
|
|
}
|
|
if (obj_inst_id == *sec_obj_inst) {
|
|
continue;
|
|
}
|
|
|
|
if (is_bootsrap_server(obj_inst_id)) {
|
|
*sec_obj_inst = obj_inst_id;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
LOG_WRN("No Bootstrap servers found.");
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* state machine step functions */
|
|
|
|
static int sm_do_init(void)
|
|
{
|
|
lwm2m_engine_stop(client.ctx);
|
|
client.trigger_update = false;
|
|
client.lifetime = 0U;
|
|
client.last_update = 0U;
|
|
client.close_socket = false;
|
|
|
|
/* Do bootstrap or registration */
|
|
if (client.use_bootstrap && IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) {
|
|
set_sm_state(ENGINE_DO_BOOTSTRAP_REG);
|
|
} else {
|
|
set_sm_state(ENGINE_DO_REGISTRATION);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
static int sm_send_bootstrap_registration(void)
|
|
{
|
|
struct lwm2m_message *msg;
|
|
int ret;
|
|
|
|
msg = rd_get_message();
|
|
if (!msg) {
|
|
LOG_ERR("Unable to get a lwm2m message!");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
msg->type = COAP_TYPE_CON;
|
|
msg->code = COAP_METHOD_POST;
|
|
msg->mid = coap_next_id();
|
|
msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
|
|
msg->reply_cb = do_bootstrap_reply_cb;
|
|
msg->message_timeout_cb = do_bootstrap_reg_timeout_cb;
|
|
|
|
ret = lwm2m_init_message(msg);
|
|
if (ret) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
|
|
"bs", strlen("bs"));
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
snprintk(query_buffer, sizeof(query_buffer) - 1, "ep=%s",
|
|
client.ep_name);
|
|
|
|
ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY,
|
|
query_buffer, strlen(query_buffer));
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1)) {
|
|
int pct = LWM2M_FORMAT_OMA_TLV;
|
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)) {
|
|
pct = LWM2M_FORMAT_APP_SENML_CBOR;
|
|
} else if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)) {
|
|
pct = LWM2M_FORMAT_APP_SEML_JSON;
|
|
}
|
|
|
|
snprintk(query_buffer, sizeof(query_buffer) - 1, "pct=%d", pct);
|
|
|
|
coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY,
|
|
query_buffer, strlen(query_buffer));
|
|
}
|
|
|
|
/* log the bootstrap attempt */
|
|
LOG_DBG("Register ID with bootstrap server as '%s'",
|
|
query_buffer);
|
|
|
|
lwm2m_send_message_async(msg);
|
|
|
|
return 0;
|
|
|
|
cleanup:
|
|
lwm2m_reset_message(msg, true);
|
|
return ret;
|
|
}
|
|
|
|
static void sm_do_bootstrap_reg(void)
|
|
{
|
|
int ret;
|
|
|
|
/* clear out existing connection data */
|
|
if (client.ctx->sock_fd > -1) {
|
|
lwm2m_engine_stop(client.ctx);
|
|
}
|
|
|
|
client.ctx->bootstrap_mode = true;
|
|
ret = sm_next_bootstrap_inst(&client.ctx->sec_obj_inst);
|
|
if (ret < 0) {
|
|
set_sm_state(ENGINE_NETWORK_ERROR);
|
|
return;
|
|
}
|
|
|
|
LOG_INF("Bootstrap started with endpoint '%s' using security object %d",
|
|
client.ep_name, client.ctx->sec_obj_inst);
|
|
|
|
ret = lwm2m_engine_start(client.ctx);
|
|
if (ret < 0) {
|
|
LOG_ERR("Cannot init LWM2M engine (%d)", ret);
|
|
set_sm_state(ENGINE_NETWORK_ERROR);
|
|
return;
|
|
}
|
|
|
|
ret = sm_send_bootstrap_registration();
|
|
if (!ret) {
|
|
set_sm_state(ENGINE_BOOTSTRAP_REG_SENT);
|
|
} else {
|
|
LOG_ERR("Bootstrap registration err: %d", ret);
|
|
set_sm_state(ENGINE_NETWORK_ERROR);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void engine_bootstrap_finish(void)
|
|
{
|
|
LOG_INF("Bootstrap data transfer done!");
|
|
/* Delay the state transition, so engine have some time to send ACK
|
|
* before we close the socket
|
|
*/
|
|
set_sm_state_delayed(ENGINE_BOOTSTRAP_TRANS_DONE, DELAY_BEFORE_CLOSING);
|
|
}
|
|
|
|
static int sm_bootstrap_trans_done(void)
|
|
{
|
|
/* close down context resources */
|
|
lwm2m_engine_stop(client.ctx);
|
|
|
|
/* reset security object instance */
|
|
client.ctx->sec_obj_inst = -1;
|
|
client.use_bootstrap = false;
|
|
|
|
set_sm_state(ENGINE_DO_REGISTRATION);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int sm_send_registration(bool send_obj_support_data,
|
|
coap_reply_t reply_cb,
|
|
lwm2m_message_timeout_cb_t timeout_cb)
|
|
{
|
|
struct lwm2m_message *msg;
|
|
int ret;
|
|
char binding[CLIENT_BINDING_LEN];
|
|
char queue[CLIENT_QUEUE_LEN];
|
|
|
|
msg = rd_get_message();
|
|
if (!msg) {
|
|
LOG_ERR("Unable to get a lwm2m message!");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
msg->type = COAP_TYPE_CON;
|
|
msg->code = COAP_METHOD_POST;
|
|
msg->mid = coap_next_id();
|
|
msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
|
|
msg->reply_cb = reply_cb;
|
|
msg->message_timeout_cb = timeout_cb;
|
|
|
|
ret = lwm2m_init_message(msg);
|
|
if (ret) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
|
|
LWM2M_RD_CLIENT_URI,
|
|
strlen(LWM2M_RD_CLIENT_URI));
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (sm_is_registered()) {
|
|
ret = coap_packet_append_option(
|
|
&msg->cpkt, COAP_OPTION_URI_PATH,
|
|
client.server_ep, strlen(client.server_ep));
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (send_obj_support_data) {
|
|
ret = coap_append_option_int(
|
|
&msg->cpkt, COAP_OPTION_CONTENT_FORMAT,
|
|
LWM2M_FORMAT_APP_LINK_FORMAT);
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (!sm_is_registered()) {
|
|
snprintk(query_buffer, sizeof(query_buffer) - 1,
|
|
"lwm2m=%s", LWM2M_PROTOCOL_VERSION_STRING);
|
|
ret = coap_packet_append_option(
|
|
&msg->cpkt, COAP_OPTION_URI_QUERY,
|
|
query_buffer, strlen(query_buffer));
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
snprintk(query_buffer, sizeof(query_buffer) - 1,
|
|
"ep=%s", client.ep_name);
|
|
ret = coap_packet_append_option(
|
|
&msg->cpkt, COAP_OPTION_URI_QUERY,
|
|
query_buffer, strlen(query_buffer));
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Send lifetime only if changed or on initial registration.*/
|
|
if (sm_update_lifetime(client.ctx->srv_obj_inst, &client.lifetime) ||
|
|
!sm_is_registered()) {
|
|
snprintk(query_buffer, sizeof(query_buffer) - 1,
|
|
"lt=%d", client.lifetime);
|
|
ret = coap_packet_append_option(
|
|
&msg->cpkt, COAP_OPTION_URI_QUERY,
|
|
query_buffer, strlen(query_buffer));
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
lwm2m_engine_get_binding(binding);
|
|
lwm2m_engine_get_queue_mode(queue);
|
|
/* UDP is a default binding, no need to add option if UDP without queue is used. */
|
|
if ((!sm_is_registered() && (strcmp(binding, "U") != 0 || strcmp(queue, "Q") == 0))) {
|
|
snprintk(query_buffer, sizeof(query_buffer) - 1,
|
|
"b=%s", binding);
|
|
|
|
ret = coap_packet_append_option(
|
|
&msg->cpkt, COAP_OPTION_URI_QUERY,
|
|
query_buffer, strlen(query_buffer));
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
#if CONFIG_LWM2M_VERSION_1_1
|
|
/* In LwM2M 1.1, queue mode is a separate parameter */
|
|
uint16_t len = strlen(queue);
|
|
|
|
if (len) {
|
|
ret = coap_packet_append_option(
|
|
&msg->cpkt, COAP_OPTION_URI_QUERY,
|
|
queue, len);
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (send_obj_support_data) {
|
|
ret = coap_packet_append_payload_marker(&msg->cpkt);
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
msg->out.out_cpkt = &msg->cpkt;
|
|
msg->out.writer = &link_format_writer;
|
|
|
|
ret = do_register_op_link_format(msg);
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
lwm2m_send_message_async(msg);
|
|
|
|
/* log the registration attempt */
|
|
LOG_DBG("registration sent [%s]",
|
|
lwm2m_sprint_ip_addr(&client.ctx->remote_addr));
|
|
|
|
return 0;
|
|
|
|
cleanup:
|
|
LOG_ERR("error %d when sending registration message", ret);
|
|
lwm2m_reset_message(msg, true);
|
|
return ret;
|
|
}
|
|
|
|
static void sm_handle_registration_update_failure(void)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
LOG_WRN("Registration Update fail -> trigger full registration");
|
|
lwm2m_engine_context_close(client.ctx);
|
|
set_sm_state(ENGINE_SEND_REGISTRATION);
|
|
k_mutex_unlock(&client.mutex);
|
|
}
|
|
|
|
static int sm_send_registration_msg(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = sm_send_registration(true,
|
|
do_registration_reply_cb,
|
|
do_registration_timeout_cb);
|
|
if (!ret) {
|
|
set_sm_state(ENGINE_REGISTRATION_SENT);
|
|
} else {
|
|
LOG_ERR("Registration err: %d", ret);
|
|
set_sm_state(ENGINE_NETWORK_ERROR);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void sm_do_registration(void)
|
|
{
|
|
uint16_t ssid;
|
|
int ret = 0;
|
|
|
|
if (client.ctx->connection_suspended) {
|
|
if (lwm2m_engine_connection_resume(client.ctx)) {
|
|
lwm2m_engine_context_close(client.ctx);
|
|
/* perform full registration */
|
|
set_sm_state(ENGINE_DO_REGISTRATION);
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
bool select_srv = true;
|
|
uint16_t srv = (uint16_t) client.ctx->srv_obj_inst;
|
|
|
|
client.last_update = 0;
|
|
client.ctx->bootstrap_mode = false;
|
|
|
|
/* clear out existing connection data */
|
|
if (client.ctx->sock_fd > -1) {
|
|
if (client.close_socket) {
|
|
/* Clear old socket connection */
|
|
client.close_socket = false;
|
|
lwm2m_engine_stop(client.ctx);
|
|
} else {
|
|
lwm2m_engine_context_close(client.ctx);
|
|
/* Keep current connection, retry registration with same server */
|
|
select_srv = false;
|
|
}
|
|
}
|
|
|
|
if (select_srv) {
|
|
/* Select next one from the list, or fail */
|
|
if (!lwm2m_server_select(&srv)) {
|
|
LOG_ERR("Unable to find a valid server instance.");
|
|
goto bootstrap_or_retry;
|
|
}
|
|
|
|
client.ctx->srv_obj_inst = srv;
|
|
sm_update_lifetime(srv, &client.lifetime);
|
|
|
|
ret = lwm2m_get_u16(&LWM2M_OBJ(1, client.ctx->srv_obj_inst, 0), &ssid);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to read SSID");
|
|
lwm2m_server_disable(srv, K_FOREVER);
|
|
goto bootstrap_or_retry;
|
|
}
|
|
|
|
ret = lwm2m_security_short_id_to_inst(ssid);
|
|
if (ret < 0) {
|
|
LOG_ERR("Unable to find a valid security instance.");
|
|
lwm2m_server_disable(srv, K_FOREVER);
|
|
goto bootstrap_or_retry;
|
|
}
|
|
client.ctx->sec_obj_inst = (uint16_t) ret;
|
|
}
|
|
|
|
LOG_INF("RD Client started with endpoint '%s' with client lifetime %d using server "
|
|
"object %d",
|
|
client.ep_name, client.lifetime, client.ctx->srv_obj_inst);
|
|
|
|
ret = lwm2m_engine_start(client.ctx);
|
|
if (ret < 0) {
|
|
LOG_ERR("Cannot init LWM2M engine (%d)", ret);
|
|
goto bootstrap_or_retry;
|
|
}
|
|
}
|
|
|
|
sm_send_registration_msg();
|
|
return;
|
|
|
|
bootstrap_or_retry:
|
|
lwm2m_engine_stop(client.ctx);
|
|
if (!client.server_disabled && fallback_to_bootstrap()) {
|
|
return;
|
|
}
|
|
|
|
set_sm_state(ENGINE_NETWORK_ERROR);
|
|
}
|
|
|
|
static int64_t next_update(void)
|
|
{
|
|
int64_t next;
|
|
int64_t period = CONFIG_LWM2M_UPDATE_PERIOD;
|
|
int64_t early = CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY;
|
|
|
|
if (period == 0) {
|
|
period = client.lifetime;
|
|
}
|
|
if (early > client.lifetime) {
|
|
early = client.lifetime;
|
|
}
|
|
|
|
next = MIN(period, client.lifetime - early);
|
|
next = MAX(next, MINIMUM_PERIOD);
|
|
|
|
return client.last_update + next * MSEC_PER_SEC;
|
|
}
|
|
|
|
static int64_t next_rx_off(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
|
|
return client.last_tx + CONFIG_LWM2M_QUEUE_MODE_UPTIME * MSEC_PER_SEC;
|
|
} else {
|
|
return next_update();
|
|
}
|
|
}
|
|
|
|
/** Return timestamp to next even whether it is RX_OFF or update event */
|
|
static int64_t calc_next_event(void)
|
|
{
|
|
return Z_MIN(next_update(), next_rx_off());
|
|
}
|
|
|
|
static void sm_registration_done(void)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
|
|
int64_t now = k_uptime_get();
|
|
|
|
if (sm_is_registered() &&
|
|
(client.trigger_update ||
|
|
now >= next_update())) {
|
|
set_sm_state_delayed(ENGINE_UPDATE_REGISTRATION, DELAY_FOR_ACK);
|
|
} else if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED) &&
|
|
(client.engine_state != ENGINE_REGISTRATION_DONE_RX_OFF) &&
|
|
(now >= next_rx_off())) {
|
|
set_sm_state(ENGINE_REGISTRATION_DONE_RX_OFF);
|
|
next_event_at(next_update());
|
|
} else {
|
|
next_event_at(calc_next_event());
|
|
}
|
|
k_mutex_unlock(&client.mutex);
|
|
}
|
|
|
|
static int update_registration(void)
|
|
{
|
|
int ret;
|
|
bool update_objects;
|
|
|
|
update_objects = client.update_objects;
|
|
client.trigger_update = false;
|
|
client.update_objects = false;
|
|
|
|
ret = lwm2m_engine_connection_resume(client.ctx);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = sm_send_registration(update_objects,
|
|
do_update_reply_cb,
|
|
do_update_timeout_cb);
|
|
if (ret) {
|
|
LOG_ERR("Registration update err: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sm_update_registration(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = update_registration();
|
|
if (ret) {
|
|
LOG_ERR("Failed to update registration. Falling back to full registration");
|
|
|
|
lwm2m_engine_stop(client.ctx);
|
|
/* perform full registration */
|
|
set_sm_state(ENGINE_DO_REGISTRATION);
|
|
return ret;
|
|
}
|
|
|
|
set_sm_state(ENGINE_UPDATE_SENT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sm_do_deregister(void)
|
|
{
|
|
struct lwm2m_message *msg;
|
|
int ret;
|
|
|
|
if (lwm2m_engine_connection_resume(client.ctx)) {
|
|
lwm2m_engine_context_close(client.ctx);
|
|
/* Connection failed, enter directly to deregistered state */
|
|
set_sm_state(ENGINE_DEREGISTERED);
|
|
return 0;
|
|
}
|
|
|
|
msg = rd_get_message();
|
|
if (!msg) {
|
|
LOG_ERR("Unable to get a lwm2m message!");
|
|
ret = -ENOMEM;
|
|
goto close_ctx;
|
|
}
|
|
|
|
msg->type = COAP_TYPE_CON;
|
|
msg->code = COAP_METHOD_DELETE;
|
|
msg->mid = coap_next_id();
|
|
msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
|
|
msg->reply_cb = do_deregister_reply_cb;
|
|
msg->message_timeout_cb = do_deregister_timeout_cb;
|
|
|
|
ret = lwm2m_init_message(msg);
|
|
if (ret) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
|
|
LWM2M_RD_CLIENT_URI,
|
|
strlen(LWM2M_RD_CLIENT_URI));
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to encode URI path option (err:%d).", ret);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* include server endpoint in URI PATH */
|
|
ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
|
|
client.server_ep,
|
|
strlen(client.server_ep));
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to encode URI path option (err:%d).", ret);
|
|
goto cleanup;
|
|
}
|
|
|
|
LOG_INF("Deregister from '%s'", client.server_ep);
|
|
|
|
lwm2m_send_message_async(msg);
|
|
|
|
set_sm_state(ENGINE_DEREGISTER_SENT);
|
|
return 0;
|
|
|
|
cleanup:
|
|
lwm2m_reset_message(msg, true);
|
|
close_ctx:
|
|
lwm2m_engine_stop(client.ctx);
|
|
set_sm_state(ENGINE_DEREGISTERED);
|
|
return ret;
|
|
}
|
|
|
|
static bool fallback_to_bootstrap(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) {
|
|
bool fallback = true;
|
|
|
|
(void)lwm2m_get_bool(&LWM2M_OBJ(LWM2M_OBJECT_SERVER_ID, client.ctx->srv_obj_inst,
|
|
SERVER_BOOTSTRAP_ON_REGISTRATION_FAILURE_ID),
|
|
&fallback);
|
|
if (fallback) {
|
|
client.use_bootstrap = true;
|
|
set_sm_state(ENGINE_INIT);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void sm_do_network_error(void)
|
|
{
|
|
int err;
|
|
|
|
LOG_ERR("sm_do_network_error, retries %d", client.retries);
|
|
|
|
lwm2m_socket_close(client.ctx);
|
|
|
|
if (client.retry_delay) {
|
|
next_event_at(k_uptime_get() + client.retry_delay * MSEC_PER_SEC);
|
|
client.retry_delay = 0;
|
|
return;
|
|
}
|
|
client.retry_delay = 1 << client.retries;
|
|
client.retries++;
|
|
|
|
/* Stop retrying and try fallback */
|
|
if (client.retries > CONFIG_LWM2M_RD_CLIENT_MAX_RETRIES) {
|
|
LOG_ERR("Network error, max retries reached (%d)", client.retries);
|
|
|
|
/* Disable current server for a period so lwm2m_server_select() does not pick it */
|
|
if (client.ctx->srv_obj_inst > -1) {
|
|
lwm2m_server_disable(client.ctx->srv_obj_inst, DISABLE_TIMEOUT);
|
|
}
|
|
|
|
/* Are we in bootstrap? Try if we can fallback to some other BS server */
|
|
if (client.ctx->bootstrap_mode &&
|
|
IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) {
|
|
LOG_DBG("In bootstrap, try fallback srv");
|
|
/* Do we have any other bootstrap server to back off to? */
|
|
if (sm_next_bootstrap_inst(&client.ctx->sec_obj_inst) < 0) {
|
|
/* No, we are out of options, stop engine */
|
|
goto stop_engine;
|
|
}
|
|
set_sm_state(ENGINE_INIT);
|
|
return;
|
|
}
|
|
|
|
/* Try if there are other server to fall back to,
|
|
* Only allow fallback to higher priority server (lower value, or lower id)
|
|
* if we have successfully registered before.
|
|
* This should block us from looping the same list again.
|
|
* Instead we should fallback to bootstrap.
|
|
*/
|
|
uint16_t srv;
|
|
|
|
if (lwm2m_server_select(&srv)) {
|
|
uint8_t p1, p2;
|
|
|
|
p1 = lwm2m_server_get_prio(client.ctx->srv_obj_inst);
|
|
p2 = lwm2m_server_get_prio(srv);
|
|
if (p1 < p2 || client.last_update != 0) {
|
|
set_sm_state(ENGINE_INIT);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* If we have been disabled by some server, don't fall back to bootstrap */
|
|
if (client.server_disabled) {
|
|
set_sm_state(ENGINE_SERVER_DISABLED);
|
|
return;
|
|
}
|
|
|
|
if (fallback_to_bootstrap()) {
|
|
return;
|
|
}
|
|
goto stop_engine;
|
|
}
|
|
|
|
/* Retry bootstrap */
|
|
if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) {
|
|
if (client.ctx->bootstrap_mode) {
|
|
lwm2m_engine_context_close(client.ctx);
|
|
/* If we don't have fallback BS server, retry with current one */
|
|
if (sm_next_bootstrap_inst(&client.ctx->sec_obj_inst) < 0) {
|
|
client.ctx->sec_obj_inst = -1;
|
|
}
|
|
set_sm_state(ENGINE_DO_BOOTSTRAP_REG);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!client.last_update ||
|
|
(k_uptime_get() - client.last_update) / MSEC_PER_SEC > client.lifetime) {
|
|
/* do full registration as there is no active registration or lifetime exceeded */
|
|
/* Keep the same server until out of retry */
|
|
set_sm_state(ENGINE_DO_REGISTRATION);
|
|
return;
|
|
}
|
|
|
|
/* Try if we can recover the DTLS session and try Update.
|
|
* This might fallback into full registration on sm_handle_registration_update_failure().
|
|
*/
|
|
err = lwm2m_socket_start(client.ctx);
|
|
if (err) {
|
|
LOG_ERR("Failed to start socket %d", err);
|
|
/*
|
|
* keep this state until lifetime/retry count exceeds. Renew
|
|
* sm state to set retry_delay etc ...
|
|
*/
|
|
set_sm_state(ENGINE_NETWORK_ERROR);
|
|
return;
|
|
}
|
|
set_sm_state(ENGINE_UPDATE_REGISTRATION);
|
|
return;
|
|
|
|
stop_engine:
|
|
|
|
/* We are out of options, stop engine */
|
|
if (client.ctx->event_cb) {
|
|
if (client.ctx->bootstrap_mode) {
|
|
client.ctx->event_cb(client.ctx,
|
|
LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE);
|
|
} else {
|
|
client.ctx->event_cb(client.ctx, LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR);
|
|
}
|
|
}
|
|
set_sm_state(ENGINE_IDLE);
|
|
}
|
|
|
|
static void lwm2m_rd_client_service(struct k_work *work)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
|
|
int64_t timeout = 0;
|
|
|
|
if (client.ctx) {
|
|
LOG_DBG("State: %d", get_sm_state());
|
|
client.next_event = INT64_MAX;
|
|
switch (get_sm_state()) {
|
|
case ENGINE_IDLE:
|
|
if (client.ctx->sock_fd > -1) {
|
|
lwm2m_engine_stop(client.ctx);
|
|
}
|
|
rd_client_message_free();
|
|
break;
|
|
|
|
case ENGINE_INIT:
|
|
sm_do_init();
|
|
break;
|
|
|
|
case ENGINE_SUSPENDED:
|
|
break;
|
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
case ENGINE_DO_BOOTSTRAP_REG:
|
|
sm_do_bootstrap_reg();
|
|
break;
|
|
|
|
case ENGINE_BOOTSTRAP_REG_SENT:
|
|
/* wait for bootstrap registration done */
|
|
timeout = EXCHANGE_LIFETIME;
|
|
break;
|
|
|
|
case ENGINE_BOOTSTRAP_REG_DONE:
|
|
/* wait for transfer done */
|
|
timeout = EXCHANGE_LIFETIME;
|
|
break;
|
|
|
|
case ENGINE_BOOTSTRAP_TRANS_DONE:
|
|
sm_bootstrap_trans_done();
|
|
break;
|
|
#endif
|
|
|
|
case ENGINE_DO_REGISTRATION:
|
|
sm_do_registration();
|
|
break;
|
|
|
|
case ENGINE_SEND_REGISTRATION:
|
|
sm_send_registration_msg();
|
|
break;
|
|
|
|
case ENGINE_REGISTRATION_SENT:
|
|
/* wait registration to be done or timeout */
|
|
timeout = EXCHANGE_LIFETIME;
|
|
break;
|
|
|
|
case ENGINE_REGISTRATION_DONE:
|
|
case ENGINE_REGISTRATION_DONE_RX_OFF:
|
|
sm_registration_done();
|
|
break;
|
|
|
|
case ENGINE_UPDATE_REGISTRATION:
|
|
sm_update_registration();
|
|
break;
|
|
|
|
case ENGINE_UPDATE_SENT:
|
|
/* wait update to be done or abort */
|
|
timeout = EXCHANGE_LIFETIME;
|
|
break;
|
|
|
|
case ENGINE_SERVER_DISABLED:
|
|
if (lwm2m_server_select(NULL)) {
|
|
set_sm_state(ENGINE_INIT);
|
|
} else {
|
|
/* wait for server to be enabled. */
|
|
/*
|
|
* TODO: Once engine is converted to use timepoint_t
|
|
* this should calculate the next event from the previous server.
|
|
*/
|
|
next_event_at(k_uptime_get() + SEC_PER_MIN * MSEC_PER_SEC);
|
|
}
|
|
break;
|
|
|
|
case ENGINE_DEREGISTER:
|
|
sm_do_deregister();
|
|
break;
|
|
|
|
case ENGINE_DEREGISTER_SENT:
|
|
/* wait for deregister to be done or reset */
|
|
timeout = EXCHANGE_LIFETIME;
|
|
break;
|
|
|
|
case ENGINE_DEREGISTERED:
|
|
lwm2m_engine_stop(client.ctx);
|
|
if (client.server_disabled) {
|
|
set_sm_state(ENGINE_SERVER_DISABLED);
|
|
} else {
|
|
set_sm_state(ENGINE_IDLE);
|
|
}
|
|
break;
|
|
|
|
case ENGINE_NETWORK_ERROR:
|
|
sm_do_network_error();
|
|
break;
|
|
|
|
default:
|
|
LOG_ERR("Unhandled state: %d", get_sm_state());
|
|
|
|
}
|
|
|
|
if (timeout) {
|
|
int64_t end = client.last_state_change + timeout * MSEC_PER_SEC;
|
|
|
|
if (end < k_uptime_get()) {
|
|
LOG_DBG("State machine have timed out");
|
|
sm_handle_timeout_state(ENGINE_INIT);
|
|
} else if (client.next_event > end) {
|
|
next_event_at(end);
|
|
}
|
|
}
|
|
}
|
|
|
|
k_mutex_unlock(&client.mutex);
|
|
}
|
|
|
|
int lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, const char *ep_name,
|
|
uint32_t flags, lwm2m_ctx_event_cb_t event_cb,
|
|
lwm2m_observe_cb_t observe_cb)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
|
|
if (!IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) &&
|
|
(flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP)) {
|
|
LOG_ERR("Bootstrap support is disabled. Please enable "
|
|
"CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP.");
|
|
|
|
k_mutex_unlock(&client.mutex);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* Check client idle state or socket is still active */
|
|
|
|
if (client.ctx && (client.engine_state != ENGINE_IDLE || client.ctx->sock_fd != -1)) {
|
|
LOG_WRN("Client is already running. state %d ", client.engine_state);
|
|
k_mutex_unlock(&client.mutex);
|
|
return -EINPROGRESS;
|
|
}
|
|
|
|
/* Init Context */
|
|
lwm2m_server_reset_timestamps();
|
|
lwm2m_engine_context_init(client_ctx);
|
|
|
|
client.ctx = client_ctx;
|
|
client.ctx->sock_fd = -1;
|
|
client.ctx->fault_cb = socket_fault_cb;
|
|
client.ctx->observe_cb = observe_cb;
|
|
client.ctx->event_cb = event_cb;
|
|
client.use_bootstrap = flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP;
|
|
client.ctx->srv_obj_inst = -1;
|
|
client.ctx->sec_obj_inst = -1;
|
|
client.retries = 0;
|
|
|
|
strncpy(client.ep_name, ep_name, CLIENT_EP_LEN - 1);
|
|
client.ep_name[CLIENT_EP_LEN - 1] = '\0';
|
|
LOG_INF("Start LWM2M Client: %s", client.ep_name);
|
|
|
|
set_sm_state(ENGINE_INIT);
|
|
|
|
k_mutex_unlock(&client.mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lwm2m_rd_client_stop(struct lwm2m_ctx *client_ctx,
|
|
lwm2m_ctx_event_cb_t event_cb, bool deregister)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
|
|
if (client.ctx != client_ctx) {
|
|
k_mutex_unlock(&client.mutex);
|
|
LOG_WRN("Cannot stop. Wrong context");
|
|
return -EPERM;
|
|
}
|
|
|
|
client.ctx->event_cb = event_cb;
|
|
rd_client_message_free();
|
|
|
|
if (sm_is_registered() && deregister && !client.server_disabled) {
|
|
set_sm_state(ENGINE_DEREGISTER);
|
|
} else {
|
|
client.server_disabled = false;
|
|
set_sm_state(ENGINE_DEREGISTERED);
|
|
}
|
|
|
|
LOG_INF("Stop LWM2M Client: %s", client.ep_name);
|
|
|
|
k_mutex_unlock(&client.mutex);
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lwm2m_rd_client_pause(void)
|
|
{
|
|
enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_ENGINE_SUSPENDED;
|
|
LOG_DBG("lwm2m_rd_client_pause()");
|
|
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
|
|
if (!client.ctx) {
|
|
k_mutex_unlock(&client.mutex);
|
|
LOG_ERR("Cannot pause. No context");
|
|
return -EPERM;
|
|
} else if (sm_is_suspended()) {
|
|
k_mutex_unlock(&client.mutex);
|
|
LOG_ERR("LwM2M client already suspended");
|
|
return 0;
|
|
}
|
|
|
|
LOG_INF("Suspend client");
|
|
if (client.ctx->event_cb) {
|
|
client.ctx->event_cb(client.ctx, event);
|
|
}
|
|
|
|
/* Suspend or close the socket */
|
|
if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_CLOSE_SOCKET_AT_IDLE)) {
|
|
lwm2m_close_socket(client.ctx);
|
|
} else {
|
|
lwm2m_socket_suspend(client.ctx);
|
|
}
|
|
|
|
suspended_client_state = get_sm_state();
|
|
set_sm_state(ENGINE_SUSPENDED);
|
|
|
|
k_mutex_unlock(&client.mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lwm2m_rd_client_resume(void)
|
|
{
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
|
|
if (!client.ctx || !lwm2m_rd_client_is_suspended(client.ctx)) {
|
|
k_mutex_unlock(&client.mutex);
|
|
LOG_WRN("Cannot resume, state is not suspended");
|
|
return -EPERM;
|
|
}
|
|
|
|
LOG_INF("Resume Client state");
|
|
|
|
if (suspended_client_state == ENGINE_UPDATE_SENT) {
|
|
/* Set back to Registration done and trigger an update */
|
|
suspended_client_state = ENGINE_REGISTRATION_DONE;
|
|
}
|
|
/* Clear Possible pending RD Client message */
|
|
rd_client_message_free();
|
|
|
|
client.engine_state = suspended_client_state;
|
|
|
|
/* Do we need to resume the bootstrap? */
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
if (sm_is_bootstrap()) {
|
|
client.engine_state = ENGINE_DO_BOOTSTRAP_REG;
|
|
}
|
|
#endif
|
|
/* Or do we resume into registration state */
|
|
if (client.engine_state >= ENGINE_DO_REGISTRATION &&
|
|
client.engine_state <= ENGINE_SERVER_DISABLED) {
|
|
if (!client.last_update ||
|
|
(client.lifetime <= (k_uptime_get() - client.last_update) / MSEC_PER_SEC)) {
|
|
/* No lifetime left, register again */
|
|
client.engine_state = ENGINE_DO_REGISTRATION;
|
|
} else {
|
|
/* Resume similarly like from QUEUE mode */
|
|
client.engine_state = ENGINE_REGISTRATION_DONE_RX_OFF;
|
|
lwm2m_rd_client_connection_resume(client.ctx);
|
|
}
|
|
}
|
|
|
|
next_event_at(0);
|
|
k_mutex_unlock(&client.mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lwm2m_rd_client_server_disabled(uint16_t inst_id)
|
|
{
|
|
if (client.ctx->srv_obj_inst != inst_id) {
|
|
return -EPERM;
|
|
}
|
|
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
|
|
client.server_disabled = true;
|
|
|
|
if (sm_is_registered()) {
|
|
LOG_INF("Server disabled, deregister");
|
|
set_sm_state_delayed(ENGINE_DEREGISTER, DELAY_BEFORE_CLOSING);
|
|
} else {
|
|
LOG_INF("Server disabled");
|
|
set_sm_state(ENGINE_DEREGISTERED);
|
|
}
|
|
|
|
k_mutex_unlock(&client.mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void lwm2m_rd_client_update(void)
|
|
{
|
|
engine_trigger_update(false);
|
|
}
|
|
|
|
struct lwm2m_ctx *lwm2m_rd_client_ctx(void)
|
|
{
|
|
return client.ctx;
|
|
}
|
|
|
|
int lwm2m_rd_client_connection_resume(struct lwm2m_ctx *client_ctx)
|
|
{
|
|
if (client.ctx != client_ctx) {
|
|
return -EPERM;
|
|
}
|
|
|
|
if (client.engine_state == ENGINE_REGISTRATION_DONE_RX_OFF) {
|
|
/*
|
|
* Switch state to triggering a proper registration message
|
|
* If the socket stays open (Connection ID or no-sec), or we have TLS session cache,
|
|
* we can trigger the update, otherwise fall back to full registration.
|
|
*/
|
|
if ((IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUSPEND_SOCKET_AT_IDLE) &&
|
|
IS_ENABLED(CONFIG_LWM2M_TLS_SESSION_CACHING)) ||
|
|
(IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_STOP_POLLING_AT_IDLE) ||
|
|
IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_LISTEN_AT_IDLE)) ||
|
|
!IS_ENABLED(CONFIG_LWM2M_DTLS_SUPPORT)) {
|
|
client.engine_state = ENGINE_REGISTRATION_DONE;
|
|
if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_NO_MSG_BUFFERING)) {
|
|
/* Force online for a short period */
|
|
engine_update_tx_time();
|
|
} else {
|
|
client.trigger_update = true;
|
|
}
|
|
} else {
|
|
client.engine_state = ENGINE_DO_REGISTRATION;
|
|
}
|
|
next_event_at(0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lwm2m_rd_client_timeout(struct lwm2m_ctx *client_ctx)
|
|
{
|
|
if (client.ctx != client_ctx) {
|
|
return -EPERM;
|
|
}
|
|
|
|
if (!sm_is_registered()) {
|
|
return 0;
|
|
}
|
|
k_mutex_lock(&client.mutex, K_FOREVER);
|
|
LOG_WRN("Confirmable Timeout -> Re-connect and register");
|
|
set_sm_state(ENGINE_DO_REGISTRATION);
|
|
next_event_at(0);
|
|
k_mutex_unlock(&client.mutex);
|
|
return 0;
|
|
}
|
|
|
|
bool lwm2m_rd_client_is_registred(struct lwm2m_ctx *client_ctx)
|
|
{
|
|
if (client.ctx != client_ctx || !sm_is_registered()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
bool lwm2m_rd_client_is_suspended(struct lwm2m_ctx *client_ctx)
|
|
{
|
|
if (client.ctx != client_ctx || !sm_is_suspended()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int lwm2m_rd_client_init(void)
|
|
{
|
|
client.ctx = NULL;
|
|
client.rd_message.ctx = NULL;
|
|
client.engine_state = ENGINE_IDLE;
|
|
k_mutex_init(&client.mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sys_lwm2m_rd_client_init(void)
|
|
{
|
|
return lwm2m_rd_client_init();
|
|
}
|
|
|
|
LWM2M_ENGINE_INIT(sys_lwm2m_rd_client_init);
|