1 /*
2 * Copyright (c) 2019 Foundries.io
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * Source material for IPSO Timer object (3340):
9 * http://www.openmobilealliance.org/tech/profiles/lwm2m/3340.xml
10 */
11
12 #define LOG_MODULE_NAME net_ipso_timer
13 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
14
15 #include <logging/log.h>
16 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
17
18 #include <stdint.h>
19 #include <init.h>
20
21 #include "lwm2m_object.h"
22 #include "lwm2m_engine.h"
23 #include "lwm2m_resource_ids.h"
24
25 #define TIMER_VERSION_MAJOR 1
26 #define TIMER_VERSION_MINOR 0
27
28 #define TIMER_MAX_ID 11
29
30 #define MAX_INSTANCE_COUNT CONFIG_LWM2M_IPSO_TIMER_INSTANCE_COUNT
31
32 /*
33 * Calculate resource instances as follows:
34 * start with TIMER_MAX_ID
35 * subtract EXEC resources (1)
36 */
37 #define RESOURCE_INSTANCE_COUNT (TIMER_MAX_ID - 1)
38
39 enum ipso_timer_mode {
40 TIMER_MODE_OFF = 0,
41 TIMER_MODE_ONE_SHOT,
42 TIMER_MODE_INTERVAL, /* TODO */
43 TIMER_MODE_DELAY_ON_PICKUP, /* TODO */
44 TIMER_MODE_DELAY_ON_DROPOUT, /* TODO */
45 };
46
47 /* resource state */
48 struct ipso_timer_data {
49 float32_value_t delay_duration;
50 float32_value_t remaining_time;
51 float32_value_t min_off_time;
52 float32_value_t cumulative_time;
53
54 uint64_t trigger_offset;
55 uint32_t trigger_counter;
56 uint32_t cumulative_time_ms;
57
58 struct k_work_delayable timer_work;
59
60 uint16_t obj_inst_id;
61 uint8_t timer_mode;
62 bool enabled;
63 bool active;
64 };
65
66 static struct ipso_timer_data timer_data[MAX_INSTANCE_COUNT];
67
68 static struct lwm2m_engine_obj timer;
69 static struct lwm2m_engine_obj_field fields[] = {
70 OBJ_FIELD_DATA(DELAY_DURATION_RID, RW, FLOAT),
71 OBJ_FIELD_DATA(REMAINING_TIME_RID, R_OPT, FLOAT),
72 OBJ_FIELD_DATA(MINIMUM_OFF_TIME_RID, RW_OPT, FLOAT),
73 OBJ_FIELD_EXECUTE_OPT(TRIGGER_RID),
74 OBJ_FIELD_DATA(ON_OFF_RID, RW_OPT, BOOL),
75 OBJ_FIELD_DATA(DIGITAL_INPUT_COUNTER_RID, RW_OPT, U32), /* TODO */
76 OBJ_FIELD_DATA(CUMULATIVE_TIME_RID, RW_OPT, FLOAT),
77 OBJ_FIELD_DATA(DIGITAL_STATE_RID, R_OPT, BOOL),
78 OBJ_FIELD_DATA(COUNTER_RID, R_OPT, U32),
79 OBJ_FIELD_DATA(TIMER_MODE_RID, RW_OPT, U8),
80 OBJ_FIELD_DATA(APPLICATION_TYPE_RID, RW_OPT, STRING),
81 };
82
83 static struct lwm2m_engine_obj_inst inst[MAX_INSTANCE_COUNT];
84 static struct lwm2m_engine_res res[MAX_INSTANCE_COUNT][TIMER_MAX_ID];
85 static struct lwm2m_engine_res_inst
86 res_inst[MAX_INSTANCE_COUNT][RESOURCE_INSTANCE_COUNT];
87
ms2float(uint32_t ms,float32_value_t * f)88 static int ms2float(uint32_t ms, float32_value_t *f)
89 {
90 f->val1 = ms / MSEC_PER_SEC;
91 f->val2 = (ms % MSEC_PER_SEC) * (LWM2M_FLOAT32_DEC_MAX / MSEC_PER_SEC);
92
93 return 0;
94 }
95
float2ms(float32_value_t * f,uint32_t * ms)96 static int float2ms(float32_value_t *f, uint32_t *ms)
97 {
98 *ms = f->val1 * MSEC_PER_SEC;
99 *ms += f->val2 / (LWM2M_FLOAT32_DEC_MAX / MSEC_PER_SEC);
100
101 return 0;
102 }
103
get_timer_index(uint16_t obj_inst_id)104 static int get_timer_index(uint16_t obj_inst_id)
105 {
106 int i, ret = -ENOENT;
107
108 for (i = 0; i < MAX_INSTANCE_COUNT; i++) {
109 if (!inst[i].obj || inst[i].obj_inst_id != obj_inst_id) {
110 continue;
111 }
112
113 ret = i;
114 break;
115 }
116
117 return ret;
118 }
119
start_timer(struct ipso_timer_data * timer)120 static int start_timer(struct ipso_timer_data *timer)
121 {
122 uint32_t temp = 0U;
123 char path[MAX_RESOURCE_LEN];
124
125 /* make sure timer is enabled and not already active */
126 if (timer->timer_mode == TIMER_MODE_OFF || timer->active ||
127 !timer->enabled) {
128 return -EINVAL;
129 }
130
131 /* check min off time from last trigger_offset */
132 float2ms(&timer->min_off_time, &temp);
133 if (k_uptime_get() < timer->trigger_offset + temp) {
134 return -EINVAL;
135 }
136
137 /* TODO: check delay_duration > 0 ? other modes can it be 0? */
138
139 timer->trigger_offset = k_uptime_get();
140 timer->trigger_counter += 1U;
141
142 snprintk(path, MAX_RESOURCE_LEN, "%d/%u/%d", IPSO_OBJECT_TIMER_ID,
143 timer->obj_inst_id, DIGITAL_STATE_RID);
144 lwm2m_engine_set_bool(path, true);
145
146 float2ms(&timer->delay_duration, &temp);
147 k_work_reschedule(&timer->timer_work, K_MSEC(temp));
148
149 return 0;
150 }
151
stop_timer(struct ipso_timer_data * timer,bool cancel)152 static int stop_timer(struct ipso_timer_data *timer, bool cancel)
153 {
154 char path[MAX_RESOURCE_LEN];
155
156 /* make sure timer is active */
157 if (!timer->active) {
158 return -EINVAL;
159 }
160
161 timer->cumulative_time_ms += k_uptime_get() - timer->trigger_offset;
162 snprintk(path, MAX_RESOURCE_LEN, "%d/%u/%d", IPSO_OBJECT_TIMER_ID,
163 timer->obj_inst_id, DIGITAL_STATE_RID);
164 lwm2m_engine_set_bool(path, false);
165
166 if (cancel) {
167 k_work_cancel_delayable(&timer->timer_work);
168 }
169
170 return 0;
171 }
172
remaining_time_read_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,size_t * data_len)173 static void *remaining_time_read_cb(uint16_t obj_inst_id,
174 uint16_t res_id, uint16_t res_inst_id,
175 size_t *data_len)
176 {
177 uint32_t temp = 0U;
178 int i;
179
180 i = get_timer_index(obj_inst_id);
181 if (i < 0) {
182 return NULL;
183 }
184
185 if (timer_data[i].active) {
186 float2ms(&timer_data[i].delay_duration, &temp);
187 temp -= (k_uptime_get() - timer_data[i].trigger_offset);
188 ms2float(temp, &timer_data[i].remaining_time);
189 } else {
190 timer_data[i].remaining_time.val1 = 0;
191 timer_data[i].remaining_time.val2 = 0;
192 }
193
194 *data_len = sizeof(timer_data[i].remaining_time);
195 return &timer_data[i].remaining_time;
196 }
197
cumulative_time_read_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,size_t * data_len)198 static void *cumulative_time_read_cb(uint16_t obj_inst_id,
199 uint16_t res_id, uint16_t res_inst_id,
200 size_t *data_len)
201 {
202 int i;
203 uint32_t temp;
204
205 i = get_timer_index(obj_inst_id);
206 if (i < 0) {
207 return NULL;
208 }
209
210 temp = timer_data[i].cumulative_time_ms;
211 if (timer_data[i].active) {
212 temp += k_uptime_get() - timer_data[i].trigger_offset;
213 }
214
215 ms2float(temp, &timer_data[i].cumulative_time);
216
217 *data_len = sizeof(timer_data[i].cumulative_time);
218 return &timer_data[i].cumulative_time;
219 }
220
cumulative_time_post_write_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,uint8_t * data,uint16_t data_len,bool last_block,size_t total_size)221 static int cumulative_time_post_write_cb(uint16_t obj_inst_id,
222 uint16_t res_id, uint16_t res_inst_id,
223 uint8_t *data, uint16_t data_len,
224 bool last_block, size_t total_size)
225 {
226 int i;
227
228 i = get_timer_index(obj_inst_id);
229 if (i < 0) {
230 return i;
231 }
232
233 timer_data[i].cumulative_time_ms = 0U;
234 return 0;
235 }
236
enabled_post_write_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,uint8_t * data,uint16_t data_len,bool last_block,size_t total_size)237 static int enabled_post_write_cb(uint16_t obj_inst_id,
238 uint16_t res_id, uint16_t res_inst_id,
239 uint8_t *data, uint16_t data_len,
240 bool last_block, size_t total_size)
241 {
242 int i;
243
244 i = get_timer_index(obj_inst_id);
245 if (i < 0) {
246 return i;
247 }
248
249 /* check if timer is active and move to disabled state */
250 if (!timer_data[i].enabled && timer_data[i].active) {
251 return stop_timer(&timer_data[i], true);
252 }
253
254 return 0;
255 }
256
trigger_counter_post_write_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,uint8_t * data,uint16_t data_len,bool last_block,size_t total_size)257 static int trigger_counter_post_write_cb(uint16_t obj_inst_id,
258 uint16_t res_id, uint16_t res_inst_id,
259 uint8_t *data, uint16_t data_len,
260 bool last_block, size_t total_size)
261 {
262 int i;
263
264 i = get_timer_index(obj_inst_id);
265 if (i < 0) {
266 return i;
267 }
268
269 timer_data[i].trigger_counter = 0U;
270 return 0;
271 }
272
timer_work_cb(struct k_work * work)273 static void timer_work_cb(struct k_work *work)
274 {
275 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
276 struct ipso_timer_data *timer = CONTAINER_OF(dwork,
277 struct ipso_timer_data,
278 timer_work);
279 stop_timer(timer, false);
280 }
281
timer_trigger_cb(uint16_t obj_inst_id,uint8_t * args,uint16_t args_len)282 static int timer_trigger_cb(uint16_t obj_inst_id,
283 uint8_t *args, uint16_t args_len)
284 {
285 int i;
286
287 i = get_timer_index(obj_inst_id);
288 if (i < 0) {
289 return i;
290 }
291
292 return start_timer(&timer_data[i]);
293 }
294
timer_create(uint16_t obj_inst_id)295 static struct lwm2m_engine_obj_inst *timer_create(uint16_t obj_inst_id)
296 {
297 int index, avail = -1, i = 0, j = 0;
298
299 /* Check that there is no other instance with this ID */
300 for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
301 if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
302 LOG_ERR("Can not create instance - "
303 "already existing: %u", obj_inst_id);
304 return NULL;
305 }
306
307 /* Save first available slot index */
308 if (avail < 0 && !inst[index].obj) {
309 avail = index;
310 }
311 }
312
313 if (avail < 0) {
314 LOG_ERR("Can not create instance - no more room: %u",
315 obj_inst_id);
316 return NULL;
317 }
318
319 /* Set default values */
320 (void)memset(&timer_data[avail], 0, sizeof(timer_data[avail]));
321 k_work_init_delayable(&timer_data[avail].timer_work, timer_work_cb);
322 timer_data[avail].delay_duration.val1 = 5; /* 5 seconds */
323 timer_data[avail].enabled = true;
324 timer_data[avail].timer_mode = TIMER_MODE_ONE_SHOT;
325 timer_data[avail].obj_inst_id = obj_inst_id;
326
327 (void)memset(res[avail], 0,
328 sizeof(res[avail][0]) * ARRAY_SIZE(res[avail]));
329 init_res_instance(res_inst[avail], ARRAY_SIZE(res_inst[avail]));
330
331 /* initialize instance resource data */
332 INIT_OBJ_RES_DATA(DELAY_DURATION_RID, res[avail], i,
333 res_inst[avail], j, &timer_data[avail].delay_duration,
334 sizeof(timer_data[avail].delay_duration));
335 INIT_OBJ_RES(REMAINING_TIME_RID, res[avail], i, res_inst[avail], j, 1,
336 false, true, &timer_data[avail].remaining_time,
337 sizeof(timer_data[avail].remaining_time),
338 remaining_time_read_cb, NULL, NULL, NULL, NULL);
339 INIT_OBJ_RES_DATA(MINIMUM_OFF_TIME_RID, res[avail], i,
340 res_inst[avail], j, &timer_data[avail].min_off_time,
341 sizeof(timer_data[avail].min_off_time));
342 INIT_OBJ_RES_EXECUTE(TRIGGER_RID, res[avail], i, timer_trigger_cb);
343 INIT_OBJ_RES(ON_OFF_RID, res[avail], i, res_inst[avail], j, 1, false,
344 true, &timer_data[avail].enabled,
345 sizeof(timer_data[avail].enabled),
346 NULL, NULL, NULL, enabled_post_write_cb, NULL);
347 INIT_OBJ_RES(CUMULATIVE_TIME_RID, res[avail], i, res_inst[avail], j, 1,
348 false, true, &timer_data[avail].cumulative_time,
349 sizeof(timer_data[avail].cumulative_time),
350 cumulative_time_read_cb, NULL, NULL,
351 cumulative_time_post_write_cb, NULL);
352 INIT_OBJ_RES_DATA(DIGITAL_STATE_RID, res[avail], i,
353 res_inst[avail], j, &timer_data[avail].active,
354 sizeof(timer_data[avail].active));
355 INIT_OBJ_RES(COUNTER_RID, res[avail], i, res_inst[avail], j, 1, false,
356 true, &timer_data[avail].trigger_counter,
357 sizeof(timer_data[avail].trigger_counter),
358 NULL, NULL, NULL, trigger_counter_post_write_cb, NULL);
359 INIT_OBJ_RES_DATA(TIMER_MODE_RID, res[avail], i, res_inst[avail], j,
360 &timer_data[avail].timer_mode,
361 sizeof(timer_data[avail].timer_mode));
362 INIT_OBJ_RES_OPTDATA(APPLICATION_TYPE_RID, res[avail], i,
363 res_inst[avail], j);
364
365 inst[avail].resources = res[avail];
366 inst[avail].resource_count = i;
367
368 LOG_DBG("Create IPSO Timer instance: %d", obj_inst_id);
369
370 return &inst[avail];
371 }
372
ipso_timer_init(const struct device * dev)373 static int ipso_timer_init(const struct device *dev)
374 {
375 timer.obj_id = IPSO_OBJECT_TIMER_ID;
376 timer.version_major = TIMER_VERSION_MAJOR;
377 timer.version_minor = TIMER_VERSION_MINOR;
378 timer.is_core = false;
379 timer.fields = fields;
380 timer.field_count = ARRAY_SIZE(fields);
381 timer.max_instance_count = MAX_INSTANCE_COUNT;
382 timer.create_cb = timer_create;
383 lwm2m_register_obj(&timer);
384
385 return 0;
386 }
387
388 SYS_INIT(ipso_timer_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
389