net: lwm2m: Add support for non-periodic services

Engine now allows registering service callbacks that are
called only once on a given timestamp.
This allows tickless services to be developed.

Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
This commit is contained in:
Seppo Takalo 2023-07-27 16:18:14 +03:00 committed by Carles Cufí
parent 1dfa711167
commit 2da8844d19
3 changed files with 85 additions and 37 deletions

View File

@ -1911,21 +1911,6 @@ int lwm2m_engine_delete_res_inst(const char *pathstr);
*/
int lwm2m_delete_res_inst(const struct lwm2m_obj_path *path);
/**
* @brief Update the period of a given service.
*
* Allow the period modification on an existing service created with
* lwm2m_engine_add_service().
* Example to frequency at which a periodic_service changes it's values :
* lwm2m_engine_update_service(device_periodic_service,5*MSEC_PER_SEC);
*
* @param[in] service Handler of the periodic_service
* @param[in] period_ms New period for the periodic_service (in milliseconds)
*
* @return 0 for success or negative in case of error.
*/
int lwm2m_engine_update_service_period(k_work_handler_t service, uint32_t period_ms);
/**
* @brief Update the period of the device service.
*

View File

@ -90,8 +90,8 @@ static bool active_engine_thread;
struct service_node {
sys_snode_t node;
k_work_handler_t service_work;
uint32_t min_call_period; /* ms */
int64_t last_timestamp; /* ms */
uint32_t call_period; /* ms */
int64_t next_timestamp; /* ms */
};
static struct service_node service_node_data[MAX_PERIODIC_SERVICE];
@ -406,20 +406,18 @@ static int64_t retransmit_request(struct lwm2m_ctx *client_ctx, const int64_t ti
static int64_t engine_next_service_timestamp(void)
{
struct service_node *srv;
int64_t event, next = INT64_MAX;
int64_t next = INT64_MAX;
SYS_SLIST_FOR_EACH_CONTAINER(&engine_service_list, srv, node) {
event = srv->last_timestamp + srv->min_call_period;
if (event < next) {
next = event;
if (srv->next_timestamp < next) {
next = srv->next_timestamp;
}
}
return next;
}
int lwm2m_engine_add_service(k_work_handler_t service, uint32_t period_ms)
static int engine_add_srv(k_work_handler_t service, uint32_t period_ms, int64_t next)
{
int i;
@ -444,8 +442,8 @@ int lwm2m_engine_add_service(k_work_handler_t service, uint32_t period_ms)
}
service_node_data[i].service_work = service;
service_node_data[i].min_call_period = period_ms;
service_node_data[i].last_timestamp = 0U;
service_node_data[i].call_period = period_ms;
service_node_data[i].next_timestamp = next;
sys_slist_append(&engine_service_list, &service_node_data[i].node);
@ -454,15 +452,36 @@ int lwm2m_engine_add_service(k_work_handler_t service, uint32_t period_ms)
return 0;
}
int lwm2m_engine_add_service(k_work_handler_t service, uint32_t period_ms)
{
return engine_add_srv(service, period_ms, k_uptime_get() + period_ms);
}
int lwm2m_engine_call_at(k_work_handler_t service, int64_t timestamp)
{
return engine_add_srv(service, 0, timestamp);
}
int lwm2m_engine_call_now(k_work_handler_t service)
{
return engine_add_srv(service, 0, k_uptime_get());
}
int lwm2m_engine_update_service_period(k_work_handler_t service, uint32_t period_ms)
{
int i = 0;
for (i = 0; i < MAX_PERIODIC_SERVICE; i++) {
if (service_node_data[i].service_work == service) {
service_node_data[i].min_call_period = period_ms;
lwm2m_engine_wake_up();
return 0;
if (period_ms) {
service_node_data[i].call_period = period_ms;
service_node_data[i].next_timestamp = k_uptime_get() + period_ms;
lwm2m_engine_wake_up();
return 0;
}
sys_slist_find_and_remove(&engine_service_list, &service_node_data[i].node);
service_node_data[i].service_work = NULL;
return 1;
}
}
@ -471,17 +490,31 @@ int lwm2m_engine_update_service_period(k_work_handler_t service, uint32_t period
static int64_t lwm2m_engine_service(const int64_t timestamp)
{
struct service_node *srv;
int64_t service_due_timestamp;
struct service_node *srv, *tmp;
bool restart;
SYS_SLIST_FOR_EACH_CONTAINER(&engine_service_list, srv, node) {
service_due_timestamp = srv->last_timestamp + srv->min_call_period;
/* service is due */
if (timestamp >= service_due_timestamp) {
srv->last_timestamp = k_uptime_get();
srv->service_work(NULL);
do {
restart = false;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&engine_service_list, srv, tmp, node) {
/* service is due */
if (timestamp >= srv->next_timestamp) {
k_work_handler_t work = srv->service_work;
if (srv->call_period) {
srv->next_timestamp = k_uptime_get() + srv->call_period;
} else {
sys_slist_find_and_remove(&engine_service_list, &srv->node);
srv->service_work = NULL;
}
if (work) {
work(NULL);
}
/* List might have been modified by the callback */
restart = true;
break;
}
}
}
} while (restart);
/* calculate how long to sleep till the next service */
return engine_next_service_timestamp();

View File

@ -94,6 +94,36 @@ int bootstrap_delete(struct lwm2m_message *msg);
*/
int lwm2m_engine_add_service(k_work_handler_t service, uint32_t period_ms);
/**
* @brief Update the period of a given service or remove it.
*
* Allow the period modification on an existing service created with
* lwm2m_engine_add_service(). When period is zero, service is removed.
*
* @param[in] service Handler of the periodic_service
* @param[in] period_ms New period for the periodic_service (in milliseconds) or zero.
*
* @return 0 for success, 1 when service was removed or negative in case of error.
*/
int lwm2m_engine_update_service_period(k_work_handler_t service, uint32_t period_ms);
/**
* @brief Call specific service handler only once at given timestamp.
*
* @param[in] service service to be called
* @param[in] timestamp Time when to call
* @return 0 for success or negative in case of error
*/
int lwm2m_engine_call_at(k_work_handler_t service, int64_t timestamp);
/**
* @brief Call given handler from engine context.
*
* @param[in] service Service callback to be called.
* @return 0 for success or negative in case of error
*/
int lwm2m_engine_call_now(k_work_handler_t service);
/**
* @brief Returns the index in the security objects list corresponding to the object instance
* id given by @p obj_inst_id