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 SYS_INIT(lwm2m_firmware_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
510