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,size_t offset)237 static int package_write_cb(uint16_t obj_inst_id, uint16_t res_id,
238 			    uint16_t res_inst_id, uint8_t *data,
239 			    uint16_t data_len, bool last_block,
240 			    size_t total_size, size_t offset)
241 {
242 	uint8_t state;
243 	int ret = 0;
244 	lwm2m_engine_set_data_cb_t write_callback;
245 	lwm2m_engine_user_cb_t cancel_callback;
246 
247 	state = lwm2m_firmware_get_update_state_inst(obj_inst_id);
248 	if (state == STATE_IDLE) {
249 		/* TODO: setup timer to check download status,
250 		 * make sure it fail after timeout
251 		 */
252 		lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_DOWNLOADING);
253 	} else if (state == STATE_DOWNLOADED) {
254 		if (data_len == 0U || (data_len == 1U && data[0] == '\0')) {
255 			/* reset to state idle and result default */
256 			lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_DEFAULT);
257 			cancel_callback = lwm2m_firmware_get_cancel_cb_inst(obj_inst_id);
258 			if (cancel_callback) {
259 				ret = cancel_callback(obj_inst_id);
260 			}
261 			LOG_DBG("Update canceled by writing %d bytes", data_len);
262 			return 0;
263 		}
264 		LOG_WRN("Download has already completed");
265 		return -EPERM;
266 	} else if (state != STATE_DOWNLOADING) {
267 		LOG_WRN("Cannot download: state = %d", state);
268 		return -EPERM;
269 	}
270 
271 	write_callback = lwm2m_firmware_get_write_cb_inst(obj_inst_id);
272 	if (write_callback) {
273 		ret = write_callback(obj_inst_id, res_id, res_inst_id, data, data_len, last_block,
274 				     total_size, offset);
275 	}
276 
277 	if (ret >= 0) {
278 		if (last_block) {
279 			lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_DOWNLOADED);
280 		}
281 
282 		return 0;
283 	} else if (ret == -ENOMEM) {
284 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_OUT_OF_MEM);
285 	} else if (ret == -ENOSPC) {
286 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_NO_STORAGE);
287 		/* Response 4.13 (RFC7959, section 2.9.3) */
288 		/* TODO: should include size1 option to indicate max size */
289 		ret = -EFBIG;
290 	} else if (ret == -EFAULT) {
291 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_INTEGRITY_FAILED);
292 	} else if (ret == -ENOMSG) {
293 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_UNSUP_FW);
294 	} else {
295 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_UPDATE_FAILED);
296 	}
297 
298 	return ret;
299 }
300 
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,size_t offset)301 static int package_uri_write_cb(uint16_t obj_inst_id, uint16_t res_id,
302 				uint16_t res_inst_id, uint8_t *data,
303 				uint16_t data_len, bool last_block,
304 				size_t total_size, size_t offset)
305 {
306 	LOG_DBG("PACKAGE_URI WRITE: %s", package_uri[obj_inst_id]);
307 
308 #ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
309 	uint8_t state = lwm2m_firmware_get_update_state_inst(obj_inst_id);
310 	bool empty_uri = data_len == 0 || strnlen(data, data_len) == 0;
311 
312 	if (state == STATE_IDLE) {
313 		if (!empty_uri) {
314 			lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_DOWNLOADING);
315 			lwm2m_firmware_start_transfer(obj_inst_id, package_uri[obj_inst_id]);
316 		}
317 	} else if (state == STATE_DOWNLOADED && empty_uri) {
318 		/* reset to state idle and result default */
319 		lwm2m_firmware_set_update_result_inst(obj_inst_id, RESULT_DEFAULT);
320 	}
321 
322 	return 0;
323 #else
324 	return -EINVAL;
325 #endif
326 }
327 
lwm2m_firmware_set_write_cb(lwm2m_engine_set_data_cb_t cb)328 void lwm2m_firmware_set_write_cb(lwm2m_engine_set_data_cb_t cb)
329 {
330 	lwm2m_firmware_set_write_cb_inst(0, cb);
331 }
332 
lwm2m_firmware_get_write_cb(void)333 lwm2m_engine_set_data_cb_t lwm2m_firmware_get_write_cb(void)
334 {
335 	return lwm2m_firmware_get_write_cb_inst(0);
336 }
337 
lwm2m_firmware_set_update_cb(lwm2m_engine_execute_cb_t cb)338 void lwm2m_firmware_set_update_cb(lwm2m_engine_execute_cb_t cb)
339 {
340 	lwm2m_firmware_set_update_cb_inst(0, cb);
341 }
342 
lwm2m_firmware_get_update_cb(void)343 lwm2m_engine_execute_cb_t lwm2m_firmware_get_update_cb(void)
344 {
345 	return lwm2m_firmware_get_update_cb_inst(0);
346 }
347 
lwm2m_firmware_set_cancel_cb(lwm2m_engine_user_cb_t cb)348 void lwm2m_firmware_set_cancel_cb(lwm2m_engine_user_cb_t cb)
349 {
350 	lwm2m_firmware_set_cancel_cb_inst(0, cb);
351 }
352 
lwm2m_firmware_get_cancel_cb(void)353 lwm2m_engine_user_cb_t lwm2m_firmware_get_cancel_cb(void)
354 {
355 	return lwm2m_firmware_get_cancel_cb_inst(0);
356 }
357 
lwm2m_firmware_set_write_cb_inst(uint16_t obj_inst_id,lwm2m_engine_set_data_cb_t cb)358 void lwm2m_firmware_set_write_cb_inst(uint16_t obj_inst_id, lwm2m_engine_set_data_cb_t cb)
359 {
360 	write_cb[obj_inst_id] = cb;
361 }
362 
lwm2m_firmware_get_write_cb_inst(uint16_t obj_inst_id)363 lwm2m_engine_set_data_cb_t lwm2m_firmware_get_write_cb_inst(uint16_t obj_inst_id)
364 {
365 	return write_cb[obj_inst_id];
366 }
367 
lwm2m_firmware_set_update_cb_inst(uint16_t obj_inst_id,lwm2m_engine_execute_cb_t cb)368 void lwm2m_firmware_set_update_cb_inst(uint16_t obj_inst_id, lwm2m_engine_execute_cb_t cb)
369 {
370 	update_cb[obj_inst_id] = cb;
371 }
372 
lwm2m_firmware_get_update_cb_inst(uint16_t obj_inst_id)373 lwm2m_engine_execute_cb_t lwm2m_firmware_get_update_cb_inst(uint16_t obj_inst_id)
374 {
375 	return update_cb[obj_inst_id];
376 }
377 
lwm2m_firmware_set_cancel_cb_inst(uint16_t obj_inst_id,lwm2m_engine_user_cb_t cb)378 void lwm2m_firmware_set_cancel_cb_inst(uint16_t obj_inst_id, lwm2m_engine_user_cb_t cb)
379 {
380 	cancel_cb[obj_inst_id] = cb;
381 }
382 
lwm2m_firmware_get_cancel_cb_inst(uint16_t obj_inst_id)383 lwm2m_engine_user_cb_t lwm2m_firmware_get_cancel_cb_inst(uint16_t obj_inst_id)
384 {
385 	return cancel_cb[obj_inst_id];
386 }
387 
firmware_update_cb(uint16_t obj_inst_id,uint8_t * args,uint16_t args_len)388 static int firmware_update_cb(uint16_t obj_inst_id,
389 			      uint8_t *args, uint16_t args_len)
390 {
391 	lwm2m_engine_execute_cb_t callback;
392 	uint8_t state;
393 	int ret;
394 
395 	state = lwm2m_firmware_get_update_state_inst(obj_inst_id);
396 	if (state != STATE_DOWNLOADED) {
397 		LOG_ERR("State other than downloaded: %d", state);
398 		return -EPERM;
399 	}
400 
401 	lwm2m_firmware_set_update_state_inst(obj_inst_id, STATE_UPDATING);
402 
403 	callback = lwm2m_firmware_get_update_cb_inst(obj_inst_id);
404 	if (callback) {
405 		ret = callback(obj_inst_id, args, args_len);
406 		if (ret < 0) {
407 			LOG_ERR("Failed to update firmware: %d", ret);
408 			lwm2m_firmware_set_update_result_inst(obj_inst_id,
409 				ret == -EINVAL ? RESULT_INTEGRITY_FAILED :
410 						 RESULT_UPDATE_FAILED);
411 			return 0;
412 		}
413 	}
414 
415 	return 0;
416 }
417 
firmware_create(uint16_t obj_inst_id)418 static struct lwm2m_engine_obj_inst *firmware_create(uint16_t obj_inst_id)
419 {
420 	int index, i = 0, j = 0;
421 
422 	/* Check that there is no other instance with this ID */
423 	for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
424 		if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
425 			LOG_ERR("Can not create instance - "
426 				"already existing: %u", obj_inst_id);
427 			return NULL;
428 		}
429 	}
430 
431 	for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
432 		if (!inst[index].obj) {
433 			break;
434 		}
435 	}
436 
437 	if (index >= MAX_INSTANCE_COUNT) {
438 		LOG_ERR("Can not create instance - "
439 			"no more room: %u", obj_inst_id);
440 		return NULL;
441 	}
442 
443 	init_res_instance(res_inst[index], ARRAY_SIZE(res_inst[index]));
444 
445 	/* initialize instance resource data */
446 	INIT_OBJ_RES_OPT(FIRMWARE_PACKAGE_ID, res[index], i, res_inst[index], j, 1,
447 			 false, true, NULL, NULL, NULL, package_write_cb, NULL);
448 	INIT_OBJ_RES_LEN(FIRMWARE_PACKAGE_URI_ID, res[index], i, res_inst[index], j, 1,
449 		     false, true, package_uri[index], PACKAGE_URI_LEN, 0, NULL, NULL, NULL,
450 		     package_uri_write_cb, NULL);
451 	INIT_OBJ_RES_EXECUTE(FIRMWARE_UPDATE_ID, res[index], i, firmware_update_cb);
452 	INIT_OBJ_RES_DATA(FIRMWARE_STATE_ID, res[index], i, res_inst[index], j,
453 			  &(update_state[index]), sizeof(update_state[index]));
454 	INIT_OBJ_RES_DATA(FIRMWARE_UPDATE_RESULT_ID, res[index], i, res_inst[index], j,
455 			  &(update_result[index]), sizeof(update_result[index]));
456 	INIT_OBJ_RES_OPTDATA(FIRMWARE_PACKAGE_NAME_ID, res[index], i,
457 			     res_inst[index], j);
458 	INIT_OBJ_RES_OPTDATA(FIRMWARE_PACKAGE_VERSION_ID, res[index], i,
459 			     res_inst[index], j);
460 	INIT_OBJ_RES_MULTI_OPTDATA(FIRMWARE_UPDATE_PROTO_SUPPORT_ID, res[index], i,
461 				 res_inst[index], j, 1, false);
462 	INIT_OBJ_RES_DATA(FIRMWARE_UPDATE_DELIV_METHOD_ID, res[index], i,
463 			  res_inst[index], j, &(delivery_method[index]),
464 			  sizeof(delivery_method[index]));
465 
466 	inst[index].resources = res[index];
467 	inst[index].resource_count = i;
468 
469 	LOG_DBG("Create LWM2M firmware instance: %d", obj_inst_id);
470 	return &inst[index];
471 }
472 
lwm2m_firmware_init(void)473 static int lwm2m_firmware_init(void)
474 {
475 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
476 	int ret = 0;
477 
478 	/* Set default values */
479 	firmware.obj_id = LWM2M_OBJECT_FIRMWARE_ID;
480 	firmware.version_major = FIRMWARE_VERSION_MAJOR;
481 	firmware.version_minor = FIRMWARE_VERSION_MINOR;
482 	firmware.is_core = true;
483 	firmware.fields = fields;
484 	firmware.field_count = ARRAY_SIZE(fields);
485 	firmware.max_instance_count = MAX_INSTANCE_COUNT;
486 	firmware.create_cb = firmware_create;
487 	lwm2m_register_obj(&firmware);
488 
489 
490 	for (int idx = 0; idx < MAX_INSTANCE_COUNT; idx++) {
491 		package_uri[idx][0] = '\0';
492 
493 		/* Initialize state machine */
494 		/* TODO: should be restored from the permanent storage */
495 		update_state[idx] = STATE_IDLE;
496 		update_result[idx] = RESULT_DEFAULT;
497 #ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
498 		delivery_method[idx] = DELIVERY_METHOD_BOTH;
499 #else
500 		delivery_method[idx] = DELIVERY_METHOD_PUSH_ONLY;
501 #endif
502 		ret = lwm2m_create_obj_inst(LWM2M_OBJECT_FIRMWARE_ID, idx, &obj_inst);
503 		if (ret < 0) {
504 			LOG_DBG("Create LWM2M instance %d error: %d", idx, ret);
505 			break;
506 		}
507 	}
508 
509 	return ret;
510 }
511 
512 LWM2M_CORE_INIT(lwm2m_firmware_init);
513