1 /*
2  * Copyright (c) 2017 Linaro Limited
3  * Copyright (c) 2018-2019 Foundries.io
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define LOG_MODULE_NAME net_lwm2m_obj_firmware
9 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
10 
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
13 
14 #include <string.h>
15 #include <stdio.h>
16 #include <zephyr/init.h>
17 
18 #include "lwm2m_object.h"
19 #include "lwm2m_engine.h"
20 
21 #define FIRMWARE_VERSION_MAJOR 1
22 #define FIRMWARE_VERSION_MINOR 0
23 
24 #if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT_MULTIPLE)
25 #define MAX_INSTANCE_COUNT CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_INSTANCE_COUNT
26 #else
27 #define MAX_INSTANCE_COUNT 1
28 #endif
29 
30 /* Firmware resource IDs */
31 #define FIRMWARE_PACKAGE_ID			0
32 #define FIRMWARE_PACKAGE_URI_ID			1
33 #define FIRMWARE_UPDATE_ID			2
34 #define FIRMWARE_STATE_ID			3
35 #define FIRMWARE_UPDATE_RESULT_ID		5
36 #define FIRMWARE_PACKAGE_NAME_ID		6
37 #define FIRMWARE_PACKAGE_VERSION_ID		7
38 #define FIRMWARE_UPDATE_PROTO_SUPPORT_ID	8
39 #define FIRMWARE_UPDATE_DELIV_METHOD_ID		9
40 
41 #define FIRMWARE_MAX_ID				10
42 
43 #define DELIVERY_METHOD_PULL_ONLY		0
44 #define DELIVERY_METHOD_PUSH_ONLY		1
45 #define DELIVERY_METHOD_BOTH			2
46 
47 #define PACKAGE_URI_LEN				255
48 
49 /*
50  * Calculate resource instances as follows:
51  * start with FIRMWARE_MAX_ID
52  * subtract EXEC resources (1)
53  */
54 #define RESOURCE_INSTANCE_COUNT	(FIRMWARE_MAX_ID - 1)
55 
56 /* resource state variables */
57 static uint8_t update_state[MAX_INSTANCE_COUNT];
58 static uint8_t update_result[MAX_INSTANCE_COUNT];
59 static uint8_t delivery_method[MAX_INSTANCE_COUNT];
60 static char package_uri[MAX_INSTANCE_COUNT][PACKAGE_URI_LEN];
61 
62 /* A varying number of firmware object exists */
63 static struct lwm2m_engine_obj firmware;
64 static struct lwm2m_engine_obj_field fields[] = {
65 	OBJ_FIELD_DATA(FIRMWARE_PACKAGE_ID, W, OPAQUE),
66 	OBJ_FIELD_DATA(FIRMWARE_PACKAGE_URI_ID, RW, STRING),
67 	OBJ_FIELD_EXECUTE(FIRMWARE_UPDATE_ID),
68 	OBJ_FIELD_DATA(FIRMWARE_STATE_ID, R, U8),
69 	OBJ_FIELD_DATA(FIRMWARE_UPDATE_RESULT_ID, R, U8),
70 	OBJ_FIELD_DATA(FIRMWARE_PACKAGE_NAME_ID, R_OPT, STRING),
71 	OBJ_FIELD_DATA(FIRMWARE_PACKAGE_VERSION_ID, R_OPT, STRING),
72 	OBJ_FIELD_DATA(FIRMWARE_UPDATE_PROTO_SUPPORT_ID, R_OPT, U8),
73 	OBJ_FIELD_DATA(FIRMWARE_UPDATE_DELIV_METHOD_ID, R, U8)
74 };
75 
76 static struct lwm2m_engine_obj_inst inst[MAX_INSTANCE_COUNT];
77 static struct lwm2m_engine_res res[MAX_INSTANCE_COUNT][FIRMWARE_MAX_ID];
78 static struct lwm2m_engine_res_inst res_inst[MAX_INSTANCE_COUNT][RESOURCE_INSTANCE_COUNT];
79 
80 static lwm2m_engine_set_data_cb_t write_cb[MAX_INSTANCE_COUNT];
81 static lwm2m_engine_execute_cb_t update_cb[MAX_INSTANCE_COUNT];
82 static lwm2m_engine_user_cb_t cancel_cb[MAX_INSTANCE_COUNT];
83 
84 #ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
85 extern int lwm2m_firmware_start_transfer(uint16_t obj_inst_id, char *package_uri);
86 #endif
87 
lwm2m_firmware_get_update_state_inst(uint16_t obj_inst_id)88 uint8_t lwm2m_firmware_get_update_state_inst(uint16_t obj_inst_id)
89 {
90 	return update_state[obj_inst_id];
91 }
92 
lwm2m_firmware_get_update_state(void)93 uint8_t lwm2m_firmware_get_update_state(void)
94 {
95 	return lwm2m_firmware_get_update_state_inst(0);
96 }
97 
lwm2m_firmware_set_update_state_inst(uint16_t obj_inst_id,uint8_t state)98 void lwm2m_firmware_set_update_state_inst(uint16_t obj_inst_id, uint8_t state)
99 {
100 	bool error = false;
101 	struct lwm2m_obj_path path = LWM2M_OBJ(LWM2M_OBJECT_FIRMWARE_ID, obj_inst_id,
102 					       FIRMWARE_UPDATE_RESULT_ID);
103 
104 	lwm2m_registry_lock();
105 	/* Check LWM2M SPEC appendix E.6.1 */
106 	switch (state) {
107 	case STATE_DOWNLOADING:
108 		if (update_state[obj_inst_id] == STATE_IDLE) {
109 			lwm2m_set_u8(&path, RESULT_DEFAULT);
110 		} else {
111 			error = true;
112 		}
113 		break;
114 	case STATE_DOWNLOADED:
115 		if (update_state[obj_inst_id] == STATE_DOWNLOADING) {
116 			lwm2m_set_u8(&path, RESULT_DEFAULT);
117 		} else if (update_state[obj_inst_id] == STATE_UPDATING) {
118 			lwm2m_set_u8(&path, RESULT_UPDATE_FAILED);
119 		} else {
120 			error = true;
121 		}
122 		break;
123 	case STATE_UPDATING:
124 		if (update_state[obj_inst_id] != STATE_DOWNLOADED) {
125 			error = true;
126 		}
127 		break;
128 	case STATE_IDLE:
129 		break;
130 	default:
131 		LOG_ERR("Unhandled state: %u", state);
132 		lwm2m_registry_unlock();
133 		return;
134 	}
135 
136 	if (error) {
137 		LOG_ERR("Invalid state transition: %u -> %u",
138 			update_state[obj_inst_id], state);
139 	}
140 
141 	path.res_id = FIRMWARE_STATE_ID;
142 
143 	lwm2m_set_u8(&path, state);
144 	lwm2m_registry_unlock();
145 
146 	LOG_DBG("Update state = %d", state);
147 }
148 
lwm2m_firmware_set_update_state(uint8_t state)149 void lwm2m_firmware_set_update_state(uint8_t state)
150 {
151 	lwm2m_firmware_set_update_state_inst(0, state);
152 }
153 
lwm2m_firmware_get_update_result_inst(uint16_t obj_inst_id)154 uint8_t lwm2m_firmware_get_update_result_inst(uint16_t obj_inst_id)
155 {
156 	return update_result[obj_inst_id];
157 }
158 
lwm2m_firmware_get_update_result(void)159 uint8_t lwm2m_firmware_get_update_result(void)
160 {
161 	return lwm2m_firmware_get_update_result_inst(0);
162 }
163 
lwm2m_firmware_set_update_result_inst(uint16_t obj_inst_id,uint8_t result)164 void lwm2m_firmware_set_update_result_inst(uint16_t obj_inst_id, uint8_t result)
165 {
166 	uint8_t state;
167 	bool error = false;
168 	struct lwm2m_obj_path path = LWM2M_OBJ(LWM2M_OBJECT_FIRMWARE_ID, obj_inst_id,
169 					       FIRMWARE_UPDATE_RESULT_ID);
170 
171 	lwm2m_registry_lock();
172 	/* Check LWM2M SPEC appendix E.6.1 */
173 	switch (result) {
174 	case RESULT_DEFAULT:
175 		lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_IDLE);
176 		break;
177 	case RESULT_SUCCESS:
178 		if (update_state[obj_inst_id] != STATE_UPDATING) {
179 			error = true;
180 			state = update_state[obj_inst_id];
181 		}
182 
183 		lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_IDLE);
184 		break;
185 	case RESULT_NO_STORAGE:
186 	case RESULT_OUT_OF_MEM:
187 	case RESULT_CONNECTION_LOST:
188 	case RESULT_UNSUP_FW:
189 	case RESULT_INVALID_URI:
190 	case RESULT_UNSUP_PROTO:
191 		if (update_state[obj_inst_id] != STATE_DOWNLOADING) {
192 			error = true;
193 			state = update_state[obj_inst_id];
194 		}
195 
196 		lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_IDLE);
197 		break;
198 	case RESULT_INTEGRITY_FAILED:
199 		if (update_state[obj_inst_id] != STATE_DOWNLOADING &&
200 		    update_state[obj_inst_id] != STATE_UPDATING) {
201 			error = true;
202 			state = update_state[obj_inst_id];
203 		}
204 
205 		lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_IDLE);
206 		break;
207 	case RESULT_UPDATE_FAILED:
208 		if (update_state[obj_inst_id] != STATE_DOWNLOADING &&
209 		    update_state[obj_inst_id] != STATE_UPDATING) {
210 			error = true;
211 			state = update_state[obj_inst_id];
212 		}
213 
214 		lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_IDLE);
215 		break;
216 	default:
217 		LOG_ERR("Unhandled result: %u", result);
218 		lwm2m_registry_unlock();
219 		return;
220 	}
221 
222 	if (error) {
223 		LOG_ERR("Unexpected result(%u) set while state is %u",
224 			result, state);
225 	}
226 
227 	lwm2m_set_u8(&path, result);
228 	lwm2m_registry_unlock();
229 	LOG_DBG("Update result = %d", result);
230 }
231 
lwm2m_firmware_set_update_result(uint8_t result)232 void lwm2m_firmware_set_update_result(uint8_t result)
233 {
234 	lwm2m_firmware_set_update_result_inst(0, result);
235 }
236 
package_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 package_write_cb(uint16_t obj_inst_id, uint16_t res_id,
238 			    uint16_t res_inst_id, uint8_t *data, uint16_t data_len,
239 			    bool last_block, size_t total_size)
240 {
241 	uint8_t state;
242 	int ret = 0;
243 	lwm2m_engine_set_data_cb_t write_callback;
244 	lwm2m_engine_user_cb_t cancel_callback;
245 
246 	state = lwm2m_firmware_get_update_state_inst(obj_inst_id);
247 	if (state == STATE_IDLE) {
248 		/* TODO: setup timer to check download status,
249 		 * make sure it fail after timeout
250 		 */
251 		lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_DOWNLOADING);
252 	} else if (state == STATE_DOWNLOADED) {
253 		if (data_len == 0U || (data_len == 1U && data[0] == '\0')) {
254 			/* reset to state idle and result default */
255 			lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_DEFAULT);
256 			cancel_callback = lwm2m_firmware_get_cancel_cb_inst(obj_inst_id);
257 			if (cancel_callback) {
258 				ret = cancel_callback(obj_inst_id);
259 			}
260 			LOG_DBG("Update canceled by writing %d bytes", data_len);
261 			return 0;
262 		}
263 		LOG_WRN("Download has already completed");
264 		return -EPERM;
265 	} else if (state != STATE_DOWNLOADING) {
266 		LOG_WRN("Cannot download: state = %d", state);
267 		return -EPERM;
268 	}
269 
270 	write_callback = lwm2m_firmware_get_write_cb_inst(obj_inst_id);
271 	if (write_callback) {
272 		ret = write_callback(obj_inst_id, res_id, res_inst_id, data, data_len, last_block,
273 				     total_size);
274 	}
275 
276 	if (ret >= 0) {
277 		if (last_block) {
278 			lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_DOWNLOADED);
279 		}
280 
281 		return 0;
282 	} else if (ret == -ENOMEM) {
283 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_OUT_OF_MEM);
284 	} else if (ret == -ENOSPC) {
285 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_NO_STORAGE);
286 		/* Response 4.13 (RFC7959, section 2.9.3) */
287 		/* TODO: should include size1 option to indicate max size */
288 		ret = -EFBIG;
289 	} else if (ret == -EFAULT) {
290 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_INTEGRITY_FAILED);
291 	} else if (ret == -ENOMSG) {
292 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_UNSUP_FW);
293 	} else {
294 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_UPDATE_FAILED);
295 	}
296 
297 	return ret;
298 }
299 
package_uri_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)300 static int package_uri_write_cb(uint16_t obj_inst_id, uint16_t res_id,
301 				uint16_t res_inst_id, uint8_t *data, uint16_t data_len,
302 				bool last_block, size_t total_size)
303 {
304 	LOG_DBG("PACKAGE_URI WRITE: %s", package_uri[obj_inst_id]);
305 
306 #ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
307 	uint8_t state = lwm2m_firmware_get_update_state_inst(obj_inst_id);
308 
309 	if (state == STATE_IDLE) {
310 		if (data_len > 0) {
311 			lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_DOWNLOADING);
312 			lwm2m_firmware_start_transfer(obj_inst_id, package_uri[obj_inst_id]);
313 		}
314 	} else if (state == STATE_DOWNLOADED && data_len == 0U) {
315 		/* reset to state idle and result default */
316 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_DEFAULT);
317 	}
318 
319 	return 0;
320 #else
321 	return -EINVAL;
322 #endif
323 }
324 
lwm2m_firmware_set_write_cb(lwm2m_engine_set_data_cb_t cb)325 void lwm2m_firmware_set_write_cb(lwm2m_engine_set_data_cb_t cb)
326 {
327 	lwm2m_firmware_set_write_cb_inst(0, cb);
328 }
329 
lwm2m_firmware_get_write_cb(void)330 lwm2m_engine_set_data_cb_t lwm2m_firmware_get_write_cb(void)
331 {
332 	return lwm2m_firmware_get_write_cb_inst(0);
333 }
334 
lwm2m_firmware_set_update_cb(lwm2m_engine_execute_cb_t cb)335 void lwm2m_firmware_set_update_cb(lwm2m_engine_execute_cb_t cb)
336 {
337 	lwm2m_firmware_set_update_cb_inst(0, cb);
338 }
339 
lwm2m_firmware_get_update_cb(void)340 lwm2m_engine_execute_cb_t lwm2m_firmware_get_update_cb(void)
341 {
342 	return lwm2m_firmware_get_update_cb_inst(0);
343 }
344 
lwm2m_firmware_set_cancel_cb(lwm2m_engine_user_cb_t cb)345 void lwm2m_firmware_set_cancel_cb(lwm2m_engine_user_cb_t cb)
346 {
347 	lwm2m_firmware_set_cancel_cb_inst(0, cb);
348 }
349 
lwm2m_firmware_get_cancel_cb(void)350 lwm2m_engine_user_cb_t lwm2m_firmware_get_cancel_cb(void)
351 {
352 	return lwm2m_firmware_get_cancel_cb_inst(0);
353 }
354 
lwm2m_firmware_set_write_cb_inst(uint16_t obj_inst_id,lwm2m_engine_set_data_cb_t cb)355 void lwm2m_firmware_set_write_cb_inst(uint16_t obj_inst_id, lwm2m_engine_set_data_cb_t cb)
356 {
357 	write_cb[obj_inst_id] = cb;
358 }
359 
lwm2m_firmware_get_write_cb_inst(uint16_t obj_inst_id)360 lwm2m_engine_set_data_cb_t lwm2m_firmware_get_write_cb_inst(uint16_t obj_inst_id)
361 {
362 	return write_cb[obj_inst_id];
363 }
364 
lwm2m_firmware_set_update_cb_inst(uint16_t obj_inst_id,lwm2m_engine_execute_cb_t cb)365 void lwm2m_firmware_set_update_cb_inst(uint16_t obj_inst_id, lwm2m_engine_execute_cb_t cb)
366 {
367 	update_cb[obj_inst_id] = cb;
368 }
369 
lwm2m_firmware_get_update_cb_inst(uint16_t obj_inst_id)370 lwm2m_engine_execute_cb_t lwm2m_firmware_get_update_cb_inst(uint16_t obj_inst_id)
371 {
372 	return update_cb[obj_inst_id];
373 }
374 
lwm2m_firmware_set_cancel_cb_inst(uint16_t obj_inst_id,lwm2m_engine_user_cb_t cb)375 void lwm2m_firmware_set_cancel_cb_inst(uint16_t obj_inst_id, lwm2m_engine_user_cb_t cb)
376 {
377 	cancel_cb[obj_inst_id] = cb;
378 }
379 
lwm2m_firmware_get_cancel_cb_inst(uint16_t obj_inst_id)380 lwm2m_engine_user_cb_t lwm2m_firmware_get_cancel_cb_inst(uint16_t obj_inst_id)
381 {
382 	return cancel_cb[obj_inst_id];
383 }
384 
firmware_update_cb(uint16_t obj_inst_id,uint8_t * args,uint16_t args_len)385 static int firmware_update_cb(uint16_t obj_inst_id,
386 			      uint8_t *args, uint16_t args_len)
387 {
388 	lwm2m_engine_execute_cb_t callback;
389 	uint8_t state;
390 	int ret;
391 
392 	state = lwm2m_firmware_get_update_state_inst(obj_inst_id);
393 	if (state != STATE_DOWNLOADED) {
394 		LOG_ERR("State other than downloaded: %d", state);
395 		return -EPERM;
396 	}
397 
398 	lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_UPDATING);
399 
400 	callback = lwm2m_firmware_get_update_cb_inst(obj_inst_id);
401 	if (callback) {
402 		ret = callback(obj_inst_id, args, args_len);
403 		if (ret < 0) {
404 			LOG_ERR("Failed to update firmware: %d", ret);
405 			lwm2m_firmware_set_update_result_inst(obj_inst_id,
406 				ret == -EINVAL ? RESULT_INTEGRITY_FAILED :
407 						 RESULT_UPDATE_FAILED);
408 			return 0;
409 		}
410 	}
411 
412 	return 0;
413 }
414 
firmware_create(uint16_t obj_inst_id)415 static struct lwm2m_engine_obj_inst *firmware_create(uint16_t obj_inst_id)
416 {
417 	int index, i = 0, j = 0;
418 
419 	/* Check that there is no other instance with this ID */
420 	for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
421 		if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
422 			LOG_ERR("Can not create instance - "
423 				"already existing: %u", obj_inst_id);
424 			return NULL;
425 		}
426 	}
427 
428 	for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
429 		if (!inst[index].obj) {
430 			break;
431 		}
432 	}
433 
434 	if (index >= MAX_INSTANCE_COUNT) {
435 		LOG_ERR("Can not create instance - "
436 			"no more room: %u", obj_inst_id);
437 		return NULL;
438 	}
439 
440 	init_res_instance(res_inst[index], ARRAY_SIZE(res_inst[index]));
441 
442 	/* initialize instance resource data */
443 	INIT_OBJ_RES_OPT(FIRMWARE_PACKAGE_ID, res[index], i, res_inst[index], j, 1,
444 			 false, true, NULL, NULL, NULL, package_write_cb, NULL);
445 	INIT_OBJ_RES_LEN(FIRMWARE_PACKAGE_URI_ID, res[index], i, res_inst[index], j, 1,
446 		     false, true, package_uri[index], PACKAGE_URI_LEN, 0, NULL, NULL, NULL,
447 		     package_uri_write_cb, NULL);
448 	INIT_OBJ_RES_EXECUTE(FIRMWARE_UPDATE_ID, res[index], i, firmware_update_cb);
449 	INIT_OBJ_RES_DATA(FIRMWARE_STATE_ID, res[index], i, res_inst[index], j,
450 			  &(update_state[index]), sizeof(update_state[index]));
451 	INIT_OBJ_RES_DATA(FIRMWARE_UPDATE_RESULT_ID, res[index], i, res_inst[index], j,
452 			  &(update_result[index]), sizeof(update_result[index]));
453 	INIT_OBJ_RES_OPTDATA(FIRMWARE_PACKAGE_NAME_ID, res[index], i,
454 			     res_inst[index], j);
455 	INIT_OBJ_RES_OPTDATA(FIRMWARE_PACKAGE_VERSION_ID, res[index], i,
456 			     res_inst[index], j);
457 	INIT_OBJ_RES_MULTI_OPTDATA(FIRMWARE_UPDATE_PROTO_SUPPORT_ID, res[index], i,
458 				 res_inst[index], j, 1, false);
459 	INIT_OBJ_RES_DATA(FIRMWARE_UPDATE_DELIV_METHOD_ID, res[index], i,
460 			  res_inst[index], j, &(delivery_method[index]),
461 			  sizeof(delivery_method[index]));
462 
463 	inst[index].resources = res[index];
464 	inst[index].resource_count = i;
465 
466 	LOG_DBG("Create LWM2M firmware instance: %d", obj_inst_id);
467 	return &inst[index];
468 }
469 
lwm2m_firmware_init(void)470 static int lwm2m_firmware_init(void)
471 {
472 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
473 	int ret = 0;
474 
475 	/* Set default values */
476 	firmware.obj_id = LWM2M_OBJECT_FIRMWARE_ID;
477 	firmware.version_major = FIRMWARE_VERSION_MAJOR;
478 	firmware.version_minor = FIRMWARE_VERSION_MINOR;
479 	firmware.is_core = true;
480 	firmware.fields = fields;
481 	firmware.field_count = ARRAY_SIZE(fields);
482 	firmware.max_instance_count = MAX_INSTANCE_COUNT;
483 	firmware.create_cb = firmware_create;
484 	lwm2m_register_obj(&firmware);
485 
486 
487 	for (int idx = 0; idx < MAX_INSTANCE_COUNT; idx++) {
488 		package_uri[idx][0] = '\0';
489 
490 		/* Initialize state machine */
491 		/* TODO: should be restored from the permanent storage */
492 		update_state[idx] = STATE_IDLE;
493 		update_result[idx] = RESULT_DEFAULT;
494 #ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
495 		delivery_method[idx] = DELIVERY_METHOD_BOTH;
496 #else
497 		delivery_method[idx] = DELIVERY_METHOD_PUSH_ONLY;
498 #endif
499 		ret = lwm2m_create_obj_inst(LWM2M_OBJECT_FIRMWARE_ID, idx, &obj_inst);
500 		if (ret < 0) {
501 			LOG_DBG("Create LWM2M instance %d error: %d", idx, ret);
502 			break;
503 		}
504 	}
505 
506 	return ret;
507 }
508 
509 LWM2M_CORE_INIT(lwm2m_firmware_init);
510