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