1 /*
2  * Copyright (c) 2017 Linaro Limited
3  * Copyright (c) 2018-2019 Foundries.io
4  * Copyright (c) 2023 FTP Technologies
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 /*
10  * TODO:
11  * - Configurable CURRENT_TIME notification delay
12  */
13 
14 #define LOG_MODULE_NAME net_lwm2m_obj_device
15 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
16 
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
19 
20 #include <string.h>
21 #include <stdio.h>
22 #include <zephyr/init.h>
23 #include <zephyr/settings/settings.h>
24 
25 #include "lwm2m_object.h"
26 #include "lwm2m_engine.h"
27 
28 #define DEVICE_VERSION_MAJOR 1
29 #define DEVICE_VERSION_MINOR 0
30 
31 /* Device resource IDs */
32 #define DEVICE_MANUFACTURER_ID			0
33 #define DEVICE_MODEL_NUMBER_ID			1
34 #define DEVICE_SERIAL_NUMBER_ID			2
35 #define DEVICE_FIRMWARE_VERSION_ID		3
36 #define DEVICE_REBOOT_ID			4
37 #define DEVICE_FACTORY_DEFAULT_ID		5
38 #define DEVICE_AVAILABLE_POWER_SOURCES_ID	6
39 #define DEVICE_POWER_SOURCE_VOLTAGE_ID		7
40 #define DEVICE_POWER_SOURCE_CURRENT_ID		8
41 #define DEVICE_BATTERY_LEVEL_ID			9
42 #define DEVICE_MEMORY_FREE_ID			10
43 #define DEVICE_ERROR_CODE_ID			11
44 #define DEVICE_RESET_ERROR_CODE_ID		12
45 #define DEVICE_CURRENT_TIME_ID			13
46 #define DEVICE_UTC_OFFSET_ID			14
47 #define DEVICE_TIMEZONE_ID			15
48 #define DEVICE_SUPPORTED_BINDING_MODES_ID	16
49 #define DEVICE_TYPE_ID				17
50 #define DEVICE_HARDWARE_VERSION_ID		18
51 #define DEVICE_SOFTWARE_VERSION_ID		19
52 #define DEVICE_BATTERY_STATUS_ID		20
53 #define DEVICE_MEMORY_TOTAL_ID			21
54 #define DEVICE_EXT_DEV_INFO_ID			22
55 
56 #define DEVICE_MAX_ID				23
57 
58 #ifdef CONFIG_LWM2M_DEVICE_ERROR_CODE_MAX
59 #define DEVICE_ERROR_CODE_MAX	CONFIG_LWM2M_DEVICE_ERROR_CODE_MAX
60 #else
61 #define DEVICE_ERROR_CODE_MAX	10
62 #endif
63 
64 #ifdef CONFIG_LWM2M_DEVICE_PWRSRC_MAX
65 #define DEVICE_PWRSRC_MAX	CONFIG_LWM2M_DEVICE_PWRSRC_MAX
66 #else
67 #define DEVICE_PWRSRC_MAX	5
68 #endif
69 
70 #ifdef CONFIG_LWM2M_DEVICE_EXT_DEV_INFO_MAX
71 #define DEVICE_EXT_DEV_INFO_MAX CONFIG_LWM2M_DEVICE_EXT_DEV_INFO_MAX
72 #else
73 #define DEVICE_EXT_DEV_INFO_MAX	1
74 #endif
75 
76 #define DEVICE_STRING_SHORT	8
77 
78 #define DEVICE_SERVICE_INTERVAL_MS (MSEC_PER_SEC * 10)
79 
80 /*
81  * Calculate resource instances as follows:
82  * start with DEVICE_MAX_ID
83  * subtract EXEC resources (3)
84  * subtract MULTI resources because their counts include 0 resource (5)
85  * add 3x DEVICE_PWRSRC_MAX for POWER SOURCES resource instances
86  * add DEVICE_ERROR_CODE_MAX for ERROR CODE resource instances
87  * add DEVICE_EXT_DEV_INFO_MAX for EXT DEV INFO  resource instances
88  */
89 #define RESOURCE_INSTANCE_COUNT	(DEVICE_MAX_ID - 3 - 5 + \
90 				 DEVICE_PWRSRC_MAX*3 + DEVICE_ERROR_CODE_MAX + \
91 				 DEVICE_EXT_DEV_INFO_MAX)
92 
93 /* resource state variables */
94 static uint8_t  error_code_list[DEVICE_ERROR_CODE_MAX] = { LWM2M_DEVICE_ERROR_NONE };
95 static time_t time_temp;
96 static time_t time_offset;
97 static uint8_t  binding_mode[DEVICE_STRING_SHORT];
98 
99 /* only 1 instance of device object exists */
100 static struct lwm2m_engine_obj device;
101 static struct lwm2m_engine_obj_field fields[] = {
102 	OBJ_FIELD_DATA(DEVICE_MANUFACTURER_ID, R_OPT, STRING),
103 	OBJ_FIELD_DATA(DEVICE_MODEL_NUMBER_ID, R_OPT, STRING),
104 	OBJ_FIELD_DATA(DEVICE_SERIAL_NUMBER_ID, R_OPT, STRING),
105 	OBJ_FIELD_DATA(DEVICE_FIRMWARE_VERSION_ID, R_OPT, STRING),
106 	OBJ_FIELD_EXECUTE_OPT(DEVICE_REBOOT_ID),
107 	OBJ_FIELD_EXECUTE_OPT(DEVICE_FACTORY_DEFAULT_ID),
108 	OBJ_FIELD_DATA(DEVICE_AVAILABLE_POWER_SOURCES_ID, R_OPT, U8),
109 	OBJ_FIELD_DATA(DEVICE_POWER_SOURCE_VOLTAGE_ID, R_OPT, S32),
110 	OBJ_FIELD_DATA(DEVICE_POWER_SOURCE_CURRENT_ID, R_OPT, S32),
111 	OBJ_FIELD_DATA(DEVICE_BATTERY_LEVEL_ID, R_OPT, U8),
112 	OBJ_FIELD_DATA(DEVICE_MEMORY_FREE_ID, R_OPT, S32),
113 	OBJ_FIELD_DATA(DEVICE_ERROR_CODE_ID, R, U8),
114 	OBJ_FIELD_EXECUTE_OPT(DEVICE_RESET_ERROR_CODE_ID),
115 	OBJ_FIELD_DATA(DEVICE_CURRENT_TIME_ID, RW_OPT, TIME),
116 	OBJ_FIELD_DATA(DEVICE_UTC_OFFSET_ID, RW_OPT, STRING),
117 	OBJ_FIELD_DATA(DEVICE_TIMEZONE_ID, RW_OPT, STRING),
118 	OBJ_FIELD_DATA(DEVICE_SUPPORTED_BINDING_MODES_ID, R, STRING),
119 	OBJ_FIELD_DATA(DEVICE_TYPE_ID, R_OPT, STRING),
120 	OBJ_FIELD_DATA(DEVICE_HARDWARE_VERSION_ID, R_OPT, STRING),
121 	OBJ_FIELD_DATA(DEVICE_SOFTWARE_VERSION_ID, R_OPT, STRING),
122 	OBJ_FIELD_DATA(DEVICE_BATTERY_STATUS_ID, R_OPT, U8),
123 	OBJ_FIELD_DATA(DEVICE_MEMORY_TOTAL_ID, R_OPT, S32),
124 	OBJ_FIELD_DATA(DEVICE_EXT_DEV_INFO_ID, R_OPT, OBJLNK)
125 };
126 
127 static struct lwm2m_engine_obj_inst inst;
128 static struct lwm2m_engine_res res[DEVICE_MAX_ID];
129 static struct lwm2m_engine_res_inst res_inst[RESOURCE_INSTANCE_COUNT];
130 
131 /* save error code resource instance point so we can easily clear later */
132 static struct lwm2m_engine_res_inst *error_code_ri;
133 
134 #define SETTINGS_SUBTREE_LWM2M_OBJ_DEVICE "lwm2m_obj_dev"
135 #define ERROR_LIST_KEY "err"
136 
137 /* callbacks */
138 
reset_error_list(void)139 static void reset_error_list(void)
140 {
141 	int i;
142 
143 	/* "delete" error codes */
144 	for (i = 0; i < DEVICE_ERROR_CODE_MAX; i++) {
145 		error_code_list[i] = LWM2M_DEVICE_ERROR_NONE;
146 		error_code_ri[i].res_inst_id = RES_INSTANCE_NOT_CREATED;
147 	}
148 
149 	/* Default error code indicating no error */
150 	error_code_ri[0].res_inst_id = 0;
151 }
152 
reset_error_list_cb(uint16_t obj_inst_id,uint8_t * args,uint16_t args_len)153 static int reset_error_list_cb(uint16_t obj_inst_id,
154 			       uint8_t *args, uint16_t args_len)
155 {
156 	int ret = 0;
157 
158 	ARG_UNUSED(obj_inst_id);
159 	ARG_UNUSED(args);
160 	ARG_UNUSED(args_len);
161 
162 	reset_error_list();
163 
164 	lwm2m_notify_observer(LWM2M_OBJECT_DEVICE_ID, 0, DEVICE_ERROR_CODE_ID);
165 
166 	if (IS_ENABLED(CONFIG_LWM2M_DEVICE_ERROR_CODE_SETTINGS)) {
167 		ret = settings_delete(SETTINGS_SUBTREE_LWM2M_OBJ_DEVICE "/" ERROR_LIST_KEY);
168 		if (ret != 0) {
169 			LOG_ERR("Couldn't save error list: %d", ret);
170 		}
171 	}
172 
173 	return ret;
174 }
175 
current_time_read_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,size_t * data_len)176 static void *current_time_read_cb(uint16_t obj_inst_id, uint16_t res_id,
177 				  uint16_t res_inst_id, size_t *data_len)
178 {
179 	time_temp = time_offset + (k_uptime_get() / 1000);
180 	*data_len = sizeof(time_temp);
181 
182 	return &time_temp;
183 }
184 
current_time_pre_write_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,size_t * data_len)185 static void *current_time_pre_write_cb(uint16_t obj_inst_id, uint16_t res_id,
186 				       uint16_t res_inst_id, size_t *data_len)
187 {
188 	*data_len = sizeof(time_temp);
189 	return &time_temp;
190 }
191 
current_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)192 static int current_time_post_write_cb(uint16_t obj_inst_id, uint16_t res_id,
193 				      uint16_t res_inst_id,
194 				      uint8_t *data, uint16_t data_len,
195 				      bool last_block, size_t total_size)
196 {
197 	if (data_len == 4U) {
198 		time_offset = *(uint32_t *)data - (uint32_t)(k_uptime_get() / 1000);
199 		return 0;
200 	} else if (data_len == 8U) {
201 		time_offset = *(time_t *)data - (time_t)(k_uptime_get() / 1000);
202 		return 0;
203 	}
204 
205 	LOG_ERR("unknown size %u", data_len);
206 	return -EINVAL;
207 }
208 
209 /* error code function */
lwm2m_device_add_err(uint8_t error_code)210 int lwm2m_device_add_err(uint8_t error_code)
211 {
212 	int ret = 0;
213 	int i;
214 
215 	for (i = 0; i < DEVICE_ERROR_CODE_MAX; i++) {
216 		if (error_code_list[i] == LWM2M_DEVICE_ERROR_NONE) {
217 			break;
218 		}
219 
220 		/* No duplicate error codes allowed */
221 		if (error_code_list[i] == error_code) {
222 			return 0;
223 		}
224 	}
225 
226 	if (i >= DEVICE_ERROR_CODE_MAX) {
227 		return -ENOMEM;
228 	}
229 
230 	error_code_list[i] = error_code;
231 	error_code_ri[i].res_inst_id = i;
232 	lwm2m_notify_observer(LWM2M_OBJECT_DEVICE_ID, 0, DEVICE_ERROR_CODE_ID);
233 
234 	if (IS_ENABLED(CONFIG_LWM2M_DEVICE_ERROR_CODE_SETTINGS)) {
235 		ret = settings_save_one(SETTINGS_SUBTREE_LWM2M_OBJ_DEVICE "/" ERROR_LIST_KEY,
236 					error_code_list, i + 1);
237 		if (ret != 0) {
238 			LOG_ERR("Couldn't save error list: %d", ret);
239 		}
240 	}
241 
242 	return ret;
243 }
244 
device_periodic_service(struct k_work * work)245 static void device_periodic_service(struct k_work *work)
246 {
247 	lwm2m_notify_observer(LWM2M_OBJECT_DEVICE_ID, 0, DEVICE_CURRENT_TIME_ID);
248 }
249 
lwm2m_update_device_service_period(uint32_t period_ms)250 int lwm2m_update_device_service_period(uint32_t period_ms)
251 {
252 	return lwm2m_engine_update_service_period(device_periodic_service, period_ms);
253 }
254 
lwm2m_obj_device_settings_set(const char * name,size_t len,settings_read_cb read_cb,void * cb_arg)255 static int lwm2m_obj_device_settings_set(const char *name, size_t len,
256 					 settings_read_cb read_cb, void *cb_arg)
257 {
258 	const char *next;
259 	int rc;
260 	int i;
261 
262 	if (settings_name_steq(name, ERROR_LIST_KEY, &next) && !next) {
263 		if (len > sizeof(error_code_list)) {
264 			LOG_ERR("Error code list too large: %zu", len);
265 			return -EINVAL;
266 		}
267 
268 		rc = read_cb(cb_arg, error_code_list, sizeof(error_code_list));
269 		if (rc == 0) {
270 			reset_error_list();
271 			return 0;
272 		} else if (rc > 0) {
273 			for (i = 0; i < ARRAY_SIZE(error_code_list); i++) {
274 				if (i < rc) {
275 					error_code_ri[i].res_inst_id = i;
276 				} else {
277 					/* Reset remaining error code instances */
278 					error_code_list[i] = LWM2M_DEVICE_ERROR_NONE;
279 					error_code_ri[i].res_inst_id = RES_INSTANCE_NOT_CREATED;
280 				}
281 			}
282 			return 0;
283 		}
284 
285 		LOG_ERR("Error code list read failure: %d", rc);
286 
287 		return rc;
288 	}
289 
290 	return -ENOENT;
291 }
292 
293 static struct settings_handler lwm2m_obj_device_settings_handler = {
294 	.name = SETTINGS_SUBTREE_LWM2M_OBJ_DEVICE,
295 	.h_set = lwm2m_obj_device_settings_set,
296 };
297 
device_create(uint16_t obj_inst_id)298 static struct lwm2m_engine_obj_inst *device_create(uint16_t obj_inst_id)
299 {
300 	int i = 0, j = 0;
301 
302 	init_res_instance(res_inst, ARRAY_SIZE(res_inst));
303 
304 	/* initialize instance resource data */
305 	INIT_OBJ_RES_OPTDATA(DEVICE_MANUFACTURER_ID, res, i, res_inst, j);
306 	INIT_OBJ_RES_OPTDATA(DEVICE_MODEL_NUMBER_ID, res, i, res_inst, j);
307 	INIT_OBJ_RES_OPTDATA(DEVICE_SERIAL_NUMBER_ID, res, i, res_inst, j);
308 	INIT_OBJ_RES_OPTDATA(DEVICE_FIRMWARE_VERSION_ID, res, i, res_inst, j);
309 	INIT_OBJ_RES_EXECUTE(DEVICE_REBOOT_ID, res, i, NULL);
310 	INIT_OBJ_RES_EXECUTE(DEVICE_FACTORY_DEFAULT_ID, res, i, NULL);
311 	INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_AVAILABLE_POWER_SOURCES_ID, res, i,
312 				   res_inst, j, DEVICE_PWRSRC_MAX, false);
313 	INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_POWER_SOURCE_VOLTAGE_ID, res, i,
314 				   res_inst, j, DEVICE_PWRSRC_MAX, false);
315 	INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_POWER_SOURCE_CURRENT_ID, res, i,
316 				   res_inst, j, DEVICE_PWRSRC_MAX, false);
317 	INIT_OBJ_RES_OPTDATA(DEVICE_BATTERY_LEVEL_ID, res, i, res_inst, j);
318 	INIT_OBJ_RES_OPTDATA(DEVICE_MEMORY_FREE_ID, res, i, res_inst, j);
319 	error_code_ri = &res_inst[j];
320 	INIT_OBJ_RES_MULTI_DATA(DEVICE_ERROR_CODE_ID, res, i,
321 				res_inst, j, DEVICE_ERROR_CODE_MAX, false,
322 				error_code_list, sizeof(*error_code_list));
323 	INIT_OBJ_RES_EXECUTE(DEVICE_RESET_ERROR_CODE_ID, res, i,
324 			     reset_error_list_cb);
325 	INIT_OBJ_RES_OPT(DEVICE_CURRENT_TIME_ID, res, i, res_inst, j, 1, false,
326 			 true, current_time_read_cb, current_time_pre_write_cb,
327 			 NULL, current_time_post_write_cb, NULL);
328 	INIT_OBJ_RES_OPTDATA(DEVICE_UTC_OFFSET_ID, res, i, res_inst, j);
329 	INIT_OBJ_RES_OPTDATA(DEVICE_TIMEZONE_ID, res, i, res_inst, j);
330 	INIT_OBJ_RES_DATA_LEN(DEVICE_SUPPORTED_BINDING_MODES_ID, res, i,
331 			  res_inst, j, binding_mode, DEVICE_STRING_SHORT, strlen(binding_mode) + 1);
332 	INIT_OBJ_RES_OPTDATA(DEVICE_TYPE_ID, res, i, res_inst, j);
333 	INIT_OBJ_RES_OPTDATA(DEVICE_HARDWARE_VERSION_ID, res, i, res_inst, j);
334 	INIT_OBJ_RES_OPTDATA(DEVICE_SOFTWARE_VERSION_ID, res, i, res_inst, j);
335 	INIT_OBJ_RES_OPTDATA(DEVICE_BATTERY_STATUS_ID, res, i, res_inst, j);
336 	INIT_OBJ_RES_OPTDATA(DEVICE_MEMORY_TOTAL_ID, res, i, res_inst, j);
337 	INIT_OBJ_RES_MULTI_OPTDATA(DEVICE_EXT_DEV_INFO_ID, res, i, res_inst, j,
338 				   DEVICE_EXT_DEV_INFO_MAX, false);
339 
340 	inst.resources = res;
341 	inst.resource_count = i;
342 
343 	LOG_DBG("Create LWM2M device instance: %d", obj_inst_id);
344 	return &inst;
345 }
346 
lwm2m_device_init(void)347 static int lwm2m_device_init(void)
348 {
349 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
350 	int ret;
351 
352 	/* Set default values */
353 	time_offset = 0U;
354 	lwm2m_engine_get_binding(binding_mode);
355 
356 	/* initialize the device field data */
357 	device.obj_id = LWM2M_OBJECT_DEVICE_ID;
358 	device.version_major = DEVICE_VERSION_MAJOR;
359 	device.version_minor = DEVICE_VERSION_MINOR;
360 	device.is_core = true;
361 	device.fields = fields;
362 	device.field_count = ARRAY_SIZE(fields);
363 	device.max_instance_count = 1U;
364 	device.create_cb = device_create;
365 	lwm2m_register_obj(&device);
366 
367 	/* auto create the only instance */
368 	ret = lwm2m_create_obj_inst(LWM2M_OBJECT_DEVICE_ID, 0, &obj_inst);
369 	if (ret < 0) {
370 		LOG_DBG("Create LWM2M instance 0 error: %d", ret);
371 	}
372 
373 	/* Ensure error list is reset if not loaded from settings */
374 	reset_error_list();
375 
376 	/* Load error code resource instances */
377 	if (IS_ENABLED(CONFIG_LWM2M_DEVICE_ERROR_CODE_SETTINGS)) {
378 		ret = settings_register(&lwm2m_obj_device_settings_handler);
379 		if (ret == 0) {
380 			ret = settings_load_subtree(SETTINGS_SUBTREE_LWM2M_OBJ_DEVICE);
381 			if (ret != 0) {
382 				LOG_ERR("Settings load failed: %d", ret);
383 			}
384 		} else {
385 			LOG_ERR("Settings register failed: %d", ret);
386 		}
387 	}
388 
389 	/* call device_periodic_service() every 10 seconds */
390 	ret = lwm2m_engine_add_service(device_periodic_service,
391 				       DEVICE_SERVICE_INTERVAL_MS);
392 
393 	return ret;
394 }
395 
396 LWM2M_CORE_INIT(lwm2m_device_init);
397