net: lwm2m: Transmission state indications
Allow engine to give hints about ongoing CoAP transmissions. This information can be used to control various power saving modes for network interfaces. For example cellular networks might support release assist indicator. Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
This commit is contained in:
parent
0d650ffd26
commit
6161fbdf21
7 changed files with 155 additions and 4 deletions
|
@ -134,6 +134,22 @@ typedef void (*lwm2m_ctx_event_cb_t)(struct lwm2m_ctx *ctx,
|
||||||
enum lwm2m_rd_client_event event);
|
enum lwm2m_rd_client_event event);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Different traffic states of the LwM2M socket.
|
||||||
|
*
|
||||||
|
* This information can be used to give hints for the network interface
|
||||||
|
* that can decide what kind of power management should be used.
|
||||||
|
*
|
||||||
|
* These hints are given from CoAP layer messages, so usage of DTLS might affect the
|
||||||
|
* actual number of expected datagrams.
|
||||||
|
*/
|
||||||
|
enum lwm2m_socket_states {
|
||||||
|
LWM2M_SOCKET_STATE_ONGOING, /**< Ongoing traffic is expected. */
|
||||||
|
LWM2M_SOCKET_STATE_ONE_RESPONSE, /**< One response is expected for the next message. */
|
||||||
|
LWM2M_SOCKET_STATE_LAST, /**< Next message is the last one. */
|
||||||
|
LWM2M_SOCKET_STATE_NO_DATA, /**< No more data is expected. */
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief LwM2M context structure to maintain information for a single
|
* @brief LwM2M context structure to maintain information for a single
|
||||||
* LwM2M connection.
|
* LwM2M connection.
|
||||||
|
@ -249,6 +265,14 @@ struct lwm2m_ctx {
|
||||||
* copied into the actual resource buffer.
|
* copied into the actual resource buffer.
|
||||||
*/
|
*/
|
||||||
uint8_t validate_buf[CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE];
|
uint8_t validate_buf[CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to indicate transmission states.
|
||||||
|
* Client application may request LwM2M engine to indicate hints about
|
||||||
|
* transmission states and use that information to control various power
|
||||||
|
* saving modes.
|
||||||
|
*/
|
||||||
|
void (*set_socket_state)(int fd, enum lwm2m_socket_states state);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
CONFIG_LWM2M_QUEUE_MODE_ENABLED=y
|
CONFIG_LWM2M_QUEUE_MODE_ENABLED=y
|
||||||
CONFIG_LWM2M_QUEUE_MODE_UPTIME=20
|
CONFIG_LWM2M_QUEUE_MODE_UPTIME=20
|
||||||
|
CONFIG_LWM2M_RD_CLIENT_STOP_POLLING_AT_IDLE=y
|
||||||
|
|
||||||
# Default lifetime is 1 day
|
# Default lifetime is 1 day
|
||||||
CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME=86400
|
CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME=86400
|
||||||
# Send update once an hour
|
# Send update once an hour
|
||||||
|
|
|
@ -253,6 +253,25 @@ static void rd_client_event(struct lwm2m_ctx *client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void socket_state(int fd, enum lwm2m_socket_states state)
|
||||||
|
{
|
||||||
|
(void) fd;
|
||||||
|
switch (state) {
|
||||||
|
case LWM2M_SOCKET_STATE_ONGOING:
|
||||||
|
LOG_DBG("LWM2M_SOCKET_STATE_ONGOING");
|
||||||
|
break;
|
||||||
|
case LWM2M_SOCKET_STATE_ONE_RESPONSE:
|
||||||
|
LOG_DBG("LWM2M_SOCKET_STATE_ONE_RESPONSE");
|
||||||
|
break;
|
||||||
|
case LWM2M_SOCKET_STATE_LAST:
|
||||||
|
LOG_DBG("LWM2M_SOCKET_STATE_LAST");
|
||||||
|
break;
|
||||||
|
case LWM2M_SOCKET_STATE_NO_DATA:
|
||||||
|
LOG_DBG("LWM2M_SOCKET_STATE_NO_DATA");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void observe_cb(enum lwm2m_observe_event event,
|
static void observe_cb(enum lwm2m_observe_event event,
|
||||||
struct lwm2m_obj_path *path, void *user_data)
|
struct lwm2m_obj_path *path, void *user_data)
|
||||||
{
|
{
|
||||||
|
@ -367,6 +386,7 @@ int main(void)
|
||||||
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
|
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
|
||||||
client_ctx.tls_tag = CONFIG_LWM2M_APP_TLS_TAG;
|
client_ctx.tls_tag = CONFIG_LWM2M_APP_TLS_TAG;
|
||||||
#endif
|
#endif
|
||||||
|
client_ctx.set_socket_state = socket_state;
|
||||||
|
|
||||||
/* client_ctx.sec_obj_inst is 0 as a starting point */
|
/* client_ctx.sec_obj_inst is 0 as a starting point */
|
||||||
lwm2m_rd_client_start(&client_ctx, endpoint, flags, rd_client_event, observe_cb);
|
lwm2m_rd_client_start(&client_ctx, endpoint, flags, rd_client_event, observe_cb);
|
||||||
|
|
|
@ -197,6 +197,11 @@ int lwm2m_socket_suspend(struct lwm2m_ctx *client_ctx)
|
||||||
lwm2m_close_socket(client_ctx);
|
lwm2m_close_socket(client_ctx);
|
||||||
/* store back the socket handle */
|
/* store back the socket handle */
|
||||||
client_ctx->sock_fd = socket_temp_id;
|
client_ctx->sock_fd = socket_temp_id;
|
||||||
|
|
||||||
|
if (client_ctx->set_socket_state) {
|
||||||
|
client_ctx->set_socket_state(client_ctx->sock_fd,
|
||||||
|
LWM2M_SOCKET_STATE_NO_DATA);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -659,10 +664,10 @@ static int socket_recv_message(struct lwm2m_ctx *client_ctx)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int socket_send_message(struct lwm2m_ctx *client_ctx)
|
static int socket_send_message(struct lwm2m_ctx *ctx)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
sys_snode_t *msg_node = sys_slist_get(&client_ctx->pending_sends);
|
sys_snode_t *msg_node = sys_slist_get(&ctx->pending_sends);
|
||||||
struct lwm2m_message *msg;
|
struct lwm2m_message *msg;
|
||||||
|
|
||||||
if (!msg_node) {
|
if (!msg_node) {
|
||||||
|
@ -679,6 +684,32 @@ static int socket_send_message(struct lwm2m_ctx *client_ctx)
|
||||||
coap_pending_cycle(msg->pending);
|
coap_pending_cycle(msg->pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx->set_socket_state) {
|
||||||
|
#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED)
|
||||||
|
bool empty = sys_slist_is_empty(&ctx->pending_sends) &&
|
||||||
|
sys_slist_is_empty(&ctx->queued_messages);
|
||||||
|
#else
|
||||||
|
bool empty = sys_slist_is_empty(&ctx->pending_sends);
|
||||||
|
#endif
|
||||||
|
if (coap_pendings_count(ctx->pendings, ARRAY_SIZE(ctx->pendings)) > 1) {
|
||||||
|
empty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty) {
|
||||||
|
ctx->set_socket_state(ctx->sock_fd, LWM2M_SOCKET_STATE_ONGOING);
|
||||||
|
} else {
|
||||||
|
switch (msg->type) {
|
||||||
|
case COAP_TYPE_CON:
|
||||||
|
ctx->set_socket_state(ctx->sock_fd,
|
||||||
|
LWM2M_SOCKET_STATE_ONE_RESPONSE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ctx->set_socket_state(ctx->sock_fd, LWM2M_SOCKET_STATE_LAST);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rc = zsock_send(msg->ctx->sock_fd, msg->cpkt.data, msg->cpkt.offset, 0);
|
rc = zsock_send(msg->ctx->sock_fd, msg->cpkt.data, msg->cpkt.offset, 0);
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
|
|
@ -13,10 +13,11 @@
|
||||||
#include "lwm2m_rd_client.h"
|
#include "lwm2m_rd_client.h"
|
||||||
|
|
||||||
#include "stubs.h"
|
#include "stubs.h"
|
||||||
#if defined(CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME)
|
#if defined(CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME)
|
||||||
#include "timer_model.h"
|
#include "timer_model.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define LOG_LEVEL LOG_LEVEL_DBG
|
||||||
LOG_MODULE_REGISTER(lwm2m_engine_test);
|
LOG_MODULE_REGISTER(lwm2m_engine_test);
|
||||||
|
|
||||||
DEFINE_FFF_GLOBALS;
|
DEFINE_FFF_GLOBALS;
|
||||||
|
@ -67,7 +68,7 @@ static void test_service(struct k_work *work)
|
||||||
|
|
||||||
static void setup(void *data)
|
static void setup(void *data)
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME)
|
#if defined(CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME)
|
||||||
/* It is enough that some slow-down is happening on sleeps, it does not have to be
|
/* It is enough that some slow-down is happening on sleeps, it does not have to be
|
||||||
* real time
|
* real time
|
||||||
*/
|
*/
|
||||||
|
@ -467,3 +468,70 @@ ZTEST(lwm2m_engine, test_security)
|
||||||
zassert_equal(tls_credential_add_fake.arg1_history[2], TLS_CREDENTIAL_CA_CERTIFICATE);
|
zassert_equal(tls_credential_add_fake.arg1_history[2], TLS_CREDENTIAL_CA_CERTIFICATE);
|
||||||
zassert_equal(lwm2m_engine_stop(&ctx), 0);
|
zassert_equal(lwm2m_engine_stop(&ctx), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum lwm2m_socket_states last_state;
|
||||||
|
|
||||||
|
static void socket_state(int fd, enum lwm2m_socket_states state)
|
||||||
|
{
|
||||||
|
(void) fd;
|
||||||
|
last_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZTEST(lwm2m_engine, test_socket_state)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct lwm2m_ctx ctx = {
|
||||||
|
.remote_addr.sa_family = AF_INET,
|
||||||
|
.sock_fd = -1,
|
||||||
|
.set_socket_state = socket_state,
|
||||||
|
};
|
||||||
|
struct lwm2m_message msg1 = {
|
||||||
|
.ctx = &ctx,
|
||||||
|
.type = COAP_TYPE_CON,
|
||||||
|
};
|
||||||
|
struct lwm2m_message msg2 = msg1;
|
||||||
|
struct lwm2m_message ack = {
|
||||||
|
.ctx = &ctx,
|
||||||
|
.type = COAP_TYPE_ACK,
|
||||||
|
};
|
||||||
|
|
||||||
|
sys_slist_init(&ctx.pending_sends);
|
||||||
|
ret = lwm2m_engine_start(&ctx);
|
||||||
|
zassert_equal(ret, 0);
|
||||||
|
|
||||||
|
/* One confimable in queue, should cause ONE_RESPONSE status */
|
||||||
|
coap_pendings_count_fake.return_val = 1;
|
||||||
|
sys_slist_append(&ctx.pending_sends, &msg1.node);
|
||||||
|
set_socket_events(ZSOCK_POLLOUT);
|
||||||
|
k_sleep(K_MSEC(100));
|
||||||
|
zassert_equal(last_state, LWM2M_SOCKET_STATE_ONE_RESPONSE);
|
||||||
|
|
||||||
|
/* More than one messages in queue, not empty, should cause ONGOING */
|
||||||
|
coap_pendings_count_fake.return_val = 2;
|
||||||
|
sys_slist_append(&ctx.pending_sends, &msg1.node);
|
||||||
|
sys_slist_append(&ctx.pending_sends, &msg2.node);
|
||||||
|
set_socket_events(ZSOCK_POLLOUT);
|
||||||
|
k_sleep(K_MSEC(100));
|
||||||
|
zassert_equal(last_state, LWM2M_SOCKET_STATE_ONGOING);
|
||||||
|
|
||||||
|
/* Last out, while waiting for ACK to both, should still cause ONGOING */
|
||||||
|
coap_pendings_count_fake.return_val = 2;
|
||||||
|
set_socket_events(ZSOCK_POLLOUT);
|
||||||
|
k_sleep(K_MSEC(100));
|
||||||
|
zassert_equal(last_state, LWM2M_SOCKET_STATE_ONGOING);
|
||||||
|
|
||||||
|
/* Only one Ack transmiting, nothing expected back -> LAST */
|
||||||
|
coap_pendings_count_fake.return_val = 0;
|
||||||
|
sys_slist_append(&ctx.pending_sends, &ack.node);
|
||||||
|
set_socket_events(ZSOCK_POLLOUT);
|
||||||
|
k_sleep(K_MSEC(100));
|
||||||
|
zassert_equal(last_state, LWM2M_SOCKET_STATE_LAST);
|
||||||
|
|
||||||
|
/* Socket suspended (as in QUEUE_RX_OFF), should cause NO_DATA */
|
||||||
|
ret = lwm2m_socket_suspend(&ctx);
|
||||||
|
zassert_equal(ret, 0);
|
||||||
|
zassert_equal(last_state, LWM2M_SOCKET_STATE_NO_DATA);
|
||||||
|
|
||||||
|
ret = lwm2m_engine_stop(&ctx);
|
||||||
|
zassert_equal(ret, 0);
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ DEFINE_FAKE_VALUE_FUNC(int, lwm2m_send_message_async, struct lwm2m_message *);
|
||||||
DEFINE_FAKE_VOID_FUNC(lwm2m_registry_lock);
|
DEFINE_FAKE_VOID_FUNC(lwm2m_registry_lock);
|
||||||
DEFINE_FAKE_VOID_FUNC(lwm2m_registry_unlock);
|
DEFINE_FAKE_VOID_FUNC(lwm2m_registry_unlock);
|
||||||
DEFINE_FAKE_VALUE_FUNC(bool, coap_pending_cycle, struct coap_pending *);
|
DEFINE_FAKE_VALUE_FUNC(bool, coap_pending_cycle, struct coap_pending *);
|
||||||
|
DEFINE_FAKE_VALUE_FUNC(size_t, coap_pendings_count, struct coap_pending *, size_t);
|
||||||
DEFINE_FAKE_VALUE_FUNC(int, generate_notify_message, struct lwm2m_ctx *, struct observe_node *,
|
DEFINE_FAKE_VALUE_FUNC(int, generate_notify_message, struct lwm2m_ctx *, struct observe_node *,
|
||||||
void *);
|
void *);
|
||||||
DEFINE_FAKE_VALUE_FUNC(int64_t, engine_observe_shedule_next_event, struct observe_node *, uint16_t,
|
DEFINE_FAKE_VALUE_FUNC(int64_t, engine_observe_shedule_next_event, struct observe_node *, uint16_t,
|
||||||
|
@ -41,6 +42,7 @@ DEFINE_FAKE_VALUE_FUNC(int, lwm2m_delete_obj_inst, uint16_t, uint16_t);
|
||||||
DEFINE_FAKE_VOID_FUNC(lwm2m_clear_block_contexts);
|
DEFINE_FAKE_VOID_FUNC(lwm2m_clear_block_contexts);
|
||||||
DEFINE_FAKE_VALUE_FUNC(int, lwm2m_security_mode, struct lwm2m_ctx *);
|
DEFINE_FAKE_VALUE_FUNC(int, lwm2m_security_mode, struct lwm2m_ctx *);
|
||||||
DEFINE_FAKE_VALUE_FUNC(int, z_impl_zsock_setsockopt, int, int, int, const void *, socklen_t);
|
DEFINE_FAKE_VALUE_FUNC(int, z_impl_zsock_setsockopt, int, int, int, const void *, socklen_t);
|
||||||
|
DEFINE_FAKE_VOID_FUNC(engine_update_tx_time);
|
||||||
|
|
||||||
static sys_slist_t obs_obj_path_list = SYS_SLIST_STATIC_INIT(&obs_obj_path_list);
|
static sys_slist_t obs_obj_path_list = SYS_SLIST_STATIC_INIT(&obs_obj_path_list);
|
||||||
sys_slist_t *lwm2m_obs_obj_path_list(void)
|
sys_slist_t *lwm2m_obs_obj_path_list(void)
|
||||||
|
|
|
@ -28,6 +28,7 @@ DECLARE_FAKE_VALUE_FUNC(int, lwm2m_rd_client_resume);
|
||||||
DECLARE_FAKE_VALUE_FUNC(struct lwm2m_message *, find_msg, struct coap_pending *,
|
DECLARE_FAKE_VALUE_FUNC(struct lwm2m_message *, find_msg, struct coap_pending *,
|
||||||
struct coap_reply *);
|
struct coap_reply *);
|
||||||
DECLARE_FAKE_VOID_FUNC(coap_pending_clear, struct coap_pending *);
|
DECLARE_FAKE_VOID_FUNC(coap_pending_clear, struct coap_pending *);
|
||||||
|
DECLARE_FAKE_VALUE_FUNC(size_t, coap_pendings_count, struct coap_pending *, size_t);
|
||||||
DECLARE_FAKE_VOID_FUNC(lwm2m_reset_message, struct lwm2m_message *, bool);
|
DECLARE_FAKE_VOID_FUNC(lwm2m_reset_message, struct lwm2m_message *, bool);
|
||||||
DECLARE_FAKE_VALUE_FUNC(int, lwm2m_send_message_async, struct lwm2m_message *);
|
DECLARE_FAKE_VALUE_FUNC(int, lwm2m_send_message_async, struct lwm2m_message *);
|
||||||
DECLARE_FAKE_VOID_FUNC(lwm2m_registry_lock);
|
DECLARE_FAKE_VOID_FUNC(lwm2m_registry_lock);
|
||||||
|
@ -56,6 +57,7 @@ DECLARE_FAKE_VOID_FUNC(lwm2m_clear_block_contexts);
|
||||||
DECLARE_FAKE_VALUE_FUNC(int, z_impl_zsock_connect, int, const struct sockaddr *, socklen_t);
|
DECLARE_FAKE_VALUE_FUNC(int, z_impl_zsock_connect, int, const struct sockaddr *, socklen_t);
|
||||||
DECLARE_FAKE_VALUE_FUNC(int, lwm2m_security_mode, struct lwm2m_ctx *);
|
DECLARE_FAKE_VALUE_FUNC(int, lwm2m_security_mode, struct lwm2m_ctx *);
|
||||||
DECLARE_FAKE_VALUE_FUNC(int, z_impl_zsock_setsockopt, int, int, int, const void *, socklen_t);
|
DECLARE_FAKE_VALUE_FUNC(int, z_impl_zsock_setsockopt, int, int, int, const void *, socklen_t);
|
||||||
|
DECLARE_FAKE_VOID_FUNC(engine_update_tx_time);
|
||||||
|
|
||||||
#define DO_FOREACH_FAKE(FUNC) \
|
#define DO_FOREACH_FAKE(FUNC) \
|
||||||
do { \
|
do { \
|
||||||
|
@ -63,6 +65,7 @@ DECLARE_FAKE_VALUE_FUNC(int, z_impl_zsock_setsockopt, int, int, int, const void
|
||||||
FUNC(lwm2m_rd_client_resume) \
|
FUNC(lwm2m_rd_client_resume) \
|
||||||
FUNC(find_msg) \
|
FUNC(find_msg) \
|
||||||
FUNC(coap_pending_clear) \
|
FUNC(coap_pending_clear) \
|
||||||
|
FUNC(coap_pendings_count) \
|
||||||
FUNC(lwm2m_reset_message) \
|
FUNC(lwm2m_reset_message) \
|
||||||
FUNC(lwm2m_send_message_async) \
|
FUNC(lwm2m_send_message_async) \
|
||||||
FUNC(lwm2m_registry_lock) \
|
FUNC(lwm2m_registry_lock) \
|
||||||
|
@ -85,6 +88,7 @@ DECLARE_FAKE_VALUE_FUNC(int, z_impl_zsock_setsockopt, int, int, int, const void
|
||||||
FUNC(z_impl_zsock_connect) \
|
FUNC(z_impl_zsock_connect) \
|
||||||
FUNC(lwm2m_security_mode) \
|
FUNC(lwm2m_security_mode) \
|
||||||
FUNC(z_impl_zsock_setsockopt) \
|
FUNC(z_impl_zsock_setsockopt) \
|
||||||
|
FUNC(engine_update_tx_time) \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#endif /* STUBS_H */
|
#endif /* STUBS_H */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue