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