From f2ca6a8c22052eac8f10cdda41d477716f85eb02 Mon Sep 17 00:00:00 2001 From: Maik Vermeulen Date: Tue, 28 Sep 2021 10:11:49 +0200 Subject: [PATCH] net: lwm2m: Add observe callback for observe and notification events Added an observe callback so that the application can register to receive events like observer added/deleted, and notification acked/ timed out. The notifications can be traced back to the exact data contained within them by use of the user_data pointer. Fixes #38531. Signed-off-by: Maik Vermeulen --- include/net/lwm2m.h | 59 +++++++++++++++-- samples/net/lwm2m_client/src/lwm2m-client.c | 32 ++++++++- subsys/net/lib/lwm2m/lwm2m_engine.c | 72 +++++++++++++++------ subsys/net/lib/lwm2m/lwm2m_object.h | 8 --- subsys/net/lib/lwm2m/lwm2m_rd_client.c | 4 +- 5 files changed, 141 insertions(+), 34 deletions(-) diff --git a/include/net/lwm2m.h b/include/net/lwm2m.h index f864d92ad60..e6fe16c51a4 100644 --- a/include/net/lwm2m.h +++ b/include/net/lwm2m.h @@ -67,7 +67,37 @@ /* clang-format on */ typedef void (*lwm2m_socket_fault_cb_t)(int error); -typedef void (*lwm2m_notify_timeout_cb_t)(void); + +struct lwm2m_obj_path { + uint16_t obj_id; + uint16_t obj_inst_id; + uint16_t res_id; + uint16_t res_inst_id; + uint8_t level; /* 0/1/2/3/4 (4 = resource instance) */ +}; + +/** + * @brief Observe callback events + */ +enum lwm2m_observe_event { + LWM2M_OBSERVE_EVENT_OBSERVER_ADDED, + LWM2M_OBSERVE_EVENT_OBSERVER_REMOVED, + LWM2M_OBSERVE_EVENT_NOTIFY_ACK, + LWM2M_OBSERVE_EVENT_NOTIFY_TIMEOUT, +}; + +/** + * @brief Observe callback indicating observer adds and deletes, and + * notification ACKs and timeouts + * + * @param[in] event Observer add/delete or notification ack/timeout + * @param[in] path LwM2M path + * @param[in] user_data Pointer to user_data buffer, as provided in + * send_traceable_notification(). Used to determine for which + * data the ACKed/timed out notification was. + */ +typedef void (*lwm2m_observe_cb_t)(enum lwm2m_observe_event event, struct lwm2m_obj_path *path, + void *user_data); /** * @brief LwM2M context structure to maintain information for a single @@ -134,10 +164,10 @@ struct lwm2m_ctx { */ lwm2m_socket_fault_cb_t fault_cb; - /** Notify Timeout Callback. LwM2M processing thread will call this - * callback in case of notify timeout. + /** Callback for new or cancelled observations, and acknowledged or timed + * out notifications. */ - lwm2m_notify_timeout_cb_t notify_timeout_cb; + lwm2m_observe_cb_t observe_cb; /** Validation buffer. Used as a temporary buffer to decode the resource * value before validation. On successful validation, its content is @@ -980,9 +1010,13 @@ typedef void (*lwm2m_ctx_event_cb_t)(struct lwm2m_ctx *ctx, * @param[in] ep_name Registered endpoint name * @param[in] flags Flags used to configure current LwM2M session. * @param[in] event_cb Client event callback function + * @param[in] observe_cb Observe callback function called when an observer was + * added or deleted, and when a notification was acked or + * has timed out */ void lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, const char *ep_name, - uint32_t flags, lwm2m_ctx_event_cb_t event_cb); + uint32_t flags, lwm2m_ctx_event_cb_t event_cb, + lwm2m_observe_cb_t observe_cb); /** * @brief Stop the LwM2M RD (De-register) Client @@ -1005,5 +1039,20 @@ void lwm2m_rd_client_stop(struct lwm2m_ctx *client_ctx, */ void lwm2m_rd_client_update(void); +/** + * @brief LwM2M path maximum length + */ +#define LWM2M_MAX_PATH_STR_LEN sizeof("65535/65535/65535/65535") + +/** + * @brief Helper function to print path objects' contents to log + * + * @param[in] buf The buffer to use for formatting the string + * @param[in] path The path to stringify + * + * @return Resulting formatted path string + */ +char *lwm2m_path_log_strdup(char *buf, struct lwm2m_obj_path *path); + #endif /* ZEPHYR_INCLUDE_NET_LWM2M_H_ */ /**@} */ diff --git a/samples/net/lwm2m_client/src/lwm2m-client.c b/samples/net/lwm2m_client/src/lwm2m-client.c index a45d7bfe03c..dceef2c3c25 100644 --- a/samples/net/lwm2m_client/src/lwm2m-client.c +++ b/samples/net/lwm2m_client/src/lwm2m-client.c @@ -454,6 +454,34 @@ static void rd_client_event(struct lwm2m_ctx *client, } } +static void observe_cb(enum lwm2m_observe_event event, + struct lwm2m_obj_path *path, void *user_data) +{ + char buf[LWM2M_MAX_PATH_STR_LEN]; + + switch (event) { + + case LWM2M_OBSERVE_EVENT_OBSERVER_ADDED: + LOG_INF("Observer added for %s", lwm2m_path_log_strdup(buf, path)); + break; + + case LWM2M_OBSERVE_EVENT_OBSERVER_REMOVED: + LOG_INF("Observer removed for %s", lwm2m_path_log_strdup(buf, path)); + break; + + case LWM2M_OBSERVE_EVENT_NOTIFY_ACK: + LOG_INF("Notify acknowledged for %s", lwm2m_path_log_strdup(buf, path)); + break; + + case LWM2M_OBSERVE_EVENT_NOTIFY_TIMEOUT: + LOG_INF("Notify timeout for %s, trying registration update", + lwm2m_path_log_strdup(buf, path)); + + lwm2m_rd_client_update(); + break; + } +} + void main(void) { uint32_t flags = IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) ? @@ -496,10 +524,10 @@ void main(void) sprintf(&dev_str[i*2], "%02x", dev_id[i]); } - lwm2m_rd_client_start(&client, dev_str, flags, rd_client_event); + lwm2m_rd_client_start(&client, dev_str, flags, rd_client_event, observe_cb); #else /* client.sec_obj_inst is 0 as a starting point */ - lwm2m_rd_client_start(&client, CONFIG_BOARD, flags, rd_client_event); + lwm2m_rd_client_start(&client, CONFIG_BOARD, flags, rd_client_event, observe_cb); #endif k_sem_take(&quit_lock, K_FOREVER); diff --git a/subsys/net/lib/lwm2m/lwm2m_engine.c b/subsys/net/lib/lwm2m/lwm2m_engine.c index c0457082a9e..19fc62f2222 100644 --- a/subsys/net/lib/lwm2m/lwm2m_engine.c +++ b/subsys/net/lib/lwm2m/lwm2m_engine.c @@ -69,8 +69,6 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #define MAX_TOKEN_LEN 8 -#define LWM2M_MAX_PATH_STR_LEN sizeof("65535/65535/65535/65535") - struct observe_node { sys_snode_t node; struct lwm2m_obj_path path; @@ -521,10 +519,29 @@ static int engine_add_observer(struct lwm2m_message *msg, log_strdup(sprint_token(token, tkl)), log_strdup(lwm2m_sprint_ip_addr(&msg->ctx->remote_addr))); + if (msg->ctx->observe_cb) { + msg->ctx->observe_cb(LWM2M_OBSERVE_EVENT_OBSERVER_ADDED, &msg->path, NULL); + } + return 0; } -static int engine_remove_observer(struct lwm2m_ctx *ctx, const uint8_t *token, uint8_t tkl) +static void remove_observer_from_list(struct lwm2m_ctx *ctx, sys_snode_t *prev_node, + struct observe_node *obs) +{ + char buf[LWM2M_MAX_PATH_STR_LEN]; + + LOG_DBG("Removing observer %p for path %s", obs, lwm2m_path_log_strdup(buf, &obs->path)); + + if (ctx->observe_cb) { + ctx->observe_cb(LWM2M_OBSERVE_EVENT_OBSERVER_REMOVED, &obs->path, NULL); + } + + sys_slist_remove(&ctx->observer, prev_node, &obs->node); + (void)memset(obs, 0, sizeof(*obs)); +} + +static int engine_remove_observer_by_token(struct lwm2m_ctx *ctx, const uint8_t *token, uint8_t tkl) { struct observe_node *obs, *found_obj = NULL; sys_snode_t *prev_node = NULL; @@ -549,8 +566,7 @@ static int engine_remove_observer(struct lwm2m_ctx *ctx, const uint8_t *token, u return -ENOENT; } - sys_slist_remove(&ctx->observer, prev_node, &found_obj->node); - (void)memset(found_obj, 0, sizeof(*found_obj)); + remove_observer_from_list(ctx, prev_node, found_obj); LOG_DBG("observer '%s' removed", log_strdup(sprint_token(token, tkl))); @@ -600,8 +616,8 @@ static int engine_remove_observer_by_path(struct lwm2m_ctx *ctx, LOG_INF("Removing observer for path %s", lwm2m_path_log_strdup(buf, path)); - sys_slist_remove(&ctx->observer, prev_node, &found_obj->node); - (void)memset(found_obj, 0, sizeof(*found_obj)); + + remove_observer_from_list(ctx, prev_node, found_obj); return 0; } @@ -623,8 +639,7 @@ static void engine_remove_observer_by_id(uint16_t obj_id, int32_t obj_inst_id) continue; } - sys_slist_remove(&sock_ctx[i]->observer, prev_node, &obs->node); - (void)memset(obs, 0, sizeof(*obs)); + remove_observer_from_list(sock_ctx[i], prev_node, obs); } } } @@ -3830,7 +3845,7 @@ static int handle_request(struct coap_packet *request, } } else if (observe == 1) { /* remove observer */ - r = engine_remove_observer(msg->ctx, token, tkl); + r = engine_remove_observer_by_token(msg->ctx, token, tkl); if (r < 0) { #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH) r = engine_remove_observer_by_path(msg->ctx, @@ -4038,7 +4053,7 @@ static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, } /* skip release if reply->user_data has error condition */ - if (reply && reply->user_data != COAP_REPLY_STATUS_NONE) { + if (reply && reply->user_data == (void *)COAP_REPLY_STATUS_ERROR) { /* reset reply->user_data for next time */ reply->user_data = (void *)COAP_REPLY_STATUS_NONE; LOG_DBG("reply %p NOT removed", reply); @@ -4156,8 +4171,9 @@ static void notify_message_timeout_cb(struct lwm2m_message *msg) if (msg->ctx != NULL) { struct lwm2m_ctx *client_ctx = msg->ctx; - if (client_ctx->notify_timeout_cb != NULL) { - client_ctx->notify_timeout_cb(); + if (client_ctx->observe_cb) { + client_ctx->observe_cb(LWM2M_OBSERVE_EVENT_NOTIFY_TIMEOUT, + &msg->path, msg->reply->user_data); } } @@ -4171,6 +4187,7 @@ static int notify_message_reply_cb(const struct coap_packet *response, int ret = 0; uint8_t type, code; struct lwm2m_message *msg; + struct observe_node *obs, *found_obj = NULL; type = coap_header_get_type(response); code = coap_header_get_code(response); @@ -4181,17 +4198,32 @@ static int notify_message_reply_cb(const struct coap_packet *response, COAP_RESPONSE_CODE_DETAIL(code), log_strdup(sprint_token(reply->token, reply->tkl))); + msg = find_msg(NULL, reply); + /* remove observer on COAP_TYPE_RESET */ if (type == COAP_TYPE_RESET) { if (reply->tkl > 0) { - msg = find_msg(NULL, reply); - ret = engine_remove_observer(msg->ctx, reply->token, reply->tkl); + ret = engine_remove_observer_by_token(msg->ctx, reply->token, reply->tkl); if (ret) { LOG_ERR("remove observe error: %d", ret); } } else { LOG_ERR("notify reply missing token -- ignored."); } + } else { + SYS_SLIST_FOR_EACH_CONTAINER(&msg->ctx->observer, obs, node) { + if (memcmp(obs->token, reply->token, reply->tkl) == 0) { + found_obj = obs; + break; + } + } + + if (found_obj) { + if (msg->ctx->observe_cb) { + msg->ctx->observe_cb(LWM2M_OBSERVE_EVENT_NOTIFY_ACK, + &obs->path, reply->user_data); + } + } } return 0; @@ -4199,7 +4231,8 @@ static int notify_message_reply_cb(const struct coap_packet *response, static int generate_notify_message(struct lwm2m_ctx *ctx, struct observe_node *obs, - bool manual_trigger) + bool manual_trigger, + void *user_data) { struct lwm2m_message *msg; struct lwm2m_engine_obj_inst *obj_inst; @@ -4250,6 +4283,9 @@ static int generate_notify_message(struct lwm2m_ctx *ctx, goto cleanup; } + /* lwm2m_init_message() cleans the coap reply fields, so we assign our data here */ + msg->reply->user_data = user_data; + /* each notification should increment the obs counter */ obs->counter++; ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_OBSERVE, @@ -4374,7 +4410,7 @@ int lwm2m_engine_context_close(struct lwm2m_ctx *client_ctx) while (!sys_slist_is_empty(&client_ctx->observer)) { obs_node = sys_slist_get_not_empty(&client_ctx->observer); obs = SYS_SLIST_CONTAINER(obs_node, obs, node); - (void)memset(obs, 0, sizeof(*obs)); + remove_observer_from_list(client_ctx, NULL, obs); } for (i = 0, msg = messages; i < ARRAY_SIZE(messages); i++, msg++) { @@ -4474,7 +4510,7 @@ static void check_notifications(struct lwm2m_ctx *ctx, if (!manual_notify && !automatic_notify) { continue; } - rc = generate_notify_message(ctx, obs, manual_notify); + rc = generate_notify_message(ctx, obs, manual_notify, NULL); if (rc == -ENOMEM) { /* no memory/messages available, retry later */ return; diff --git a/subsys/net/lib/lwm2m/lwm2m_object.h b/subsys/net/lib/lwm2m/lwm2m_object.h index 75aeca41927..10bf34a54b8 100644 --- a/subsys/net/lib/lwm2m/lwm2m_object.h +++ b/subsys/net/lib/lwm2m/lwm2m_object.h @@ -148,14 +148,6 @@ struct lwm2m_message; #define LWM2M_PATH_LEVEL_RESOURCE_INST 4 /* path representing object instances */ -struct lwm2m_obj_path { - uint16_t obj_id; - uint16_t obj_inst_id; - uint16_t res_id; - uint16_t res_inst_id; - uint8_t level; /* 0/1/2/3/4 (4 = resource instance) */ -}; - #define OBJ_FIELD(_id, _perm, _type) \ { .res_id = _id, \ .permissions = LWM2M_PERM_ ## _perm, \ diff --git a/subsys/net/lib/lwm2m/lwm2m_rd_client.c b/subsys/net/lib/lwm2m/lwm2m_rd_client.c index 6b9a848ee6a..1816d85330c 100644 --- a/subsys/net/lib/lwm2m/lwm2m_rd_client.c +++ b/subsys/net/lib/lwm2m/lwm2m_rd_client.c @@ -1052,7 +1052,8 @@ static void lwm2m_rd_client_service(struct k_work *work) } void lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, const char *ep_name, - uint32_t flags, lwm2m_ctx_event_cb_t event_cb) + uint32_t flags, lwm2m_ctx_event_cb_t event_cb, + lwm2m_observe_cb_t observe_cb) { k_mutex_lock(&client.mutex, K_FOREVER); @@ -1068,6 +1069,7 @@ void lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, const char *ep_name, client.ctx = client_ctx; client.ctx->sock_fd = -1; client.ctx->fault_cb = socket_fault_cb; + client.ctx->observe_cb = observe_cb; client.event_cb = event_cb; client.use_bootstrap = flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP;