1 /*
2  * Copyright (c) 2020 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 #include <zephyr/bluetooth/mesh.h>
9 #include <zephyr/settings/settings.h>
10 #include "access.h"
11 #include "dfu.h"
12 #include "blob.h"
13 #include <zephyr/random/random.h>
14 #include <common/bt_str.h>
15 
16 #define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(bt_mesh_dfu_cli);
19 
20 #define TARGETS_FOR_EACH(cli, target)                                          \
21 	SYS_SLIST_FOR_EACH_CONTAINER(                                          \
22 		(sys_slist_t *)&((cli)->blob.inputs)->targets, target, blob.n)
23 
24 #define MSG_CTX(cli, dst)                                                      \
25 	{                                                                      \
26 		.app_idx = (cli)->blob.inputs->app_idx, .addr = dst,           \
27 		.send_ttl = (cli)->blob.inputs->ttl,                           \
28 	}
29 
30 #define DFU_CLI(blob_cli) CONTAINER_OF(blob_cli, struct bt_mesh_dfu_cli, blob)
31 
32 BUILD_ASSERT((DFU_UPDATE_START_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_START) +
33 	      BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX,
34 	     "The Firmware Update Start message does not fit into the maximum outgoing SDU size.");
35 
36 BUILD_ASSERT((DFU_UPDATE_INFO_STATUS_MSG_MINLEN +
37 	      BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_INFO_STATUS) + BT_MESH_MIC_SHORT)
38 	     <= BT_MESH_RX_SDU_MAX,
39 	     "The Firmware Update Info Status message does not fit into the maximum incoming SDU "
40 	     "size.");
41 
42 enum req {
43 	REQ_NONE,
44 	REQ_METADATA,
45 	REQ_IMG,
46 	REQ_STATUS,
47 };
48 
49 enum {
50 	FLAG_FAILED = BIT(0),
51 	FLAG_CANCELLED = BIT(1),
52 	FLAG_SKIP_CAPS_GET = BIT(2),
53 	FLAG_RESUME = BIT(3),
54 	FLAG_COMPLETED = BIT(4),
55 };
56 
57 enum {
58 	STATE_IDLE,
59 	STATE_TRANSFER,
60 	STATE_REFRESH,
61 	STATE_VERIFIED,
62 	STATE_APPLY,
63 	STATE_APPLIED,
64 	STATE_CONFIRM,
65 	STATE_CANCEL,
66 	STATE_SUSPENDED,
67 };
68 
69 static int32_t dfu_cli_timeout = (10 * MSEC_PER_SEC);
70 
target_get(struct bt_mesh_dfu_cli * cli,uint16_t addr)71 static struct bt_mesh_dfu_target *target_get(struct bt_mesh_dfu_cli *cli,
72 					     uint16_t addr)
73 {
74 	struct bt_mesh_dfu_target *target;
75 
76 	TARGETS_FOR_EACH(cli, target) {
77 		if (addr == target->blob.addr) {
78 			return target;
79 		}
80 	}
81 
82 	return NULL;
83 }
84 
target_failed(struct bt_mesh_dfu_cli * cli,struct bt_mesh_dfu_target * target,enum bt_mesh_dfu_status status)85 static void target_failed(struct bt_mesh_dfu_cli *cli,
86 			  struct bt_mesh_dfu_target *target,
87 			  enum bt_mesh_dfu_status status)
88 {
89 	target->status = status;
90 
91 	LOG_ERR("Target 0x%04x failed: %u", target->blob.addr, status);
92 
93 	/* Invalidate blob status to prevent the target from being included in
94 	 * future sending:
95 	 */
96 	if (target->blob.status == BT_MESH_BLOB_SUCCESS) {
97 		target->blob.status = BT_MESH_BLOB_ERR_INTERNAL;
98 	}
99 
100 	if (cli->cb && cli->cb->lost_target) {
101 		cli->cb->lost_target(cli, target);
102 	}
103 }
104 
dfu_complete(struct bt_mesh_dfu_cli * cli)105 static void dfu_complete(struct bt_mesh_dfu_cli *cli)
106 {
107 	LOG_DBG("");
108 
109 	if (cli->cb && cli->cb->ended) {
110 		cli->cb->ended(cli, BT_MESH_DFU_SUCCESS);
111 	}
112 }
113 
dfu_applied(struct bt_mesh_dfu_cli * cli)114 static void dfu_applied(struct bt_mesh_dfu_cli *cli)
115 {
116 	LOG_DBG("");
117 
118 	cli->xfer.state = STATE_APPLIED;
119 
120 	if (cli->cb && cli->cb->applied) {
121 		cli->cb->applied(cli);
122 	}
123 }
124 
dfu_failed(struct bt_mesh_dfu_cli * cli,enum bt_mesh_dfu_status reason)125 static void dfu_failed(struct bt_mesh_dfu_cli *cli,
126 		       enum bt_mesh_dfu_status reason)
127 {
128 	LOG_DBG("%u", reason);
129 
130 	cli->xfer.flags |= FLAG_FAILED;
131 
132 	if (cli->cb && cli->cb->ended) {
133 		cli->cb->ended(cli, reason);
134 	}
135 }
136 
req_setup(struct bt_mesh_dfu_cli * cli,enum req type,uint16_t addr,void * params)137 static int req_setup(struct bt_mesh_dfu_cli *cli, enum req type, uint16_t addr,
138 		     void *params)
139 {
140 	if (cli->req.type != REQ_NONE) {
141 		return -EBUSY;
142 	}
143 
144 	cli->req.addr = addr;
145 	cli->req.params = params;
146 	cli->req.type = type;
147 
148 	return 0;
149 }
150 
req_wait(struct bt_mesh_dfu_cli * cli,k_timeout_t timeout)151 static int req_wait(struct bt_mesh_dfu_cli *cli, k_timeout_t timeout)
152 {
153 	int err;
154 
155 	err = k_sem_take(&cli->req.sem, timeout);
156 	cli->req.type = REQ_NONE;
157 
158 	return err;
159 }
160 
targets_active(struct bt_mesh_dfu_cli * cli)161 static bool targets_active(struct bt_mesh_dfu_cli *cli)
162 {
163 	struct bt_mesh_dfu_target *target;
164 
165 	TARGETS_FOR_EACH(cli, target) {
166 		if (target->status == BT_MESH_DFU_SUCCESS) {
167 			return true;
168 		}
169 	}
170 
171 	return false;
172 }
173 
174 /*******************************************************************************
175  * Blob client
176  ******************************************************************************/
177 static void refresh(struct bt_mesh_dfu_cli *cli);
178 
blob_caps(struct bt_mesh_blob_cli * b,const struct bt_mesh_blob_cli_caps * caps)179 static void blob_caps(struct bt_mesh_blob_cli *b,
180 		      const struct bt_mesh_blob_cli_caps *caps)
181 {
182 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
183 	int err;
184 
185 	if (!caps) {
186 		dfu_failed(cli, BT_MESH_DFU_ERR_RESOURCES);
187 		return;
188 	}
189 
190 	cli->xfer.blob.block_size_log = caps->max_block_size_log;
191 	cli->xfer.blob.chunk_size = caps->max_chunk_size;
192 
193 	/* If mode is not already set and server reported it supports all modes
194 	 * default to PUSH, otherwise set value reported by server. If mode
195 	 * was set and server supports all modes, keep old value; set
196 	 * reported value otherwise.
197 	 */
198 	if (!(cli->xfer.blob.mode & BT_MESH_BLOB_XFER_MODE_ALL)) {
199 		cli->xfer.blob.mode =
200 			caps->modes == BT_MESH_BLOB_XFER_MODE_ALL ?
201 			BT_MESH_BLOB_XFER_MODE_PUSH : caps->modes;
202 	} else {
203 		cli->xfer.blob.mode =
204 			caps->modes == BT_MESH_BLOB_XFER_MODE_ALL ?
205 			cli->xfer.blob.mode : caps->modes;
206 	}
207 
208 	err = bt_mesh_blob_cli_send(b, b->inputs, &cli->xfer.blob, cli->xfer.io);
209 	if (err) {
210 		LOG_ERR("Starting BLOB xfer failed: %d", err);
211 		dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY);
212 	}
213 }
214 
blob_lost_target(struct bt_mesh_blob_cli * b,struct bt_mesh_blob_target * blobt,enum bt_mesh_blob_status reason)215 static void blob_lost_target(struct bt_mesh_blob_cli *b,
216 			     struct bt_mesh_blob_target *blobt,
217 			     enum bt_mesh_blob_status reason)
218 {
219 	struct bt_mesh_dfu_target *target =
220 		CONTAINER_OF(blobt, struct bt_mesh_dfu_target, blob);
221 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
222 
223 	if ((cli->xfer.state == STATE_CONFIRM || cli->xfer.state == STATE_APPLY) &&
224 	    target->effect == BT_MESH_DFU_EFFECT_UNPROV) {
225 		/* Reset status for such targets to use them in consequent procedures. See sections
226 		 * 7.1.2.6 and 7.1.2.9 of the MeshDFU.
227 		 */
228 		target->blob.status = BT_MESH_BLOB_SUCCESS;
229 		return;
230 	}
231 
232 	target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL);
233 }
234 
blob_suspended(struct bt_mesh_blob_cli * b)235 static void blob_suspended(struct bt_mesh_blob_cli *b)
236 {
237 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
238 
239 	LOG_DBG("BLOB transfer suspended");
240 
241 	cli->xfer.state = STATE_SUSPENDED;
242 
243 	if (cli->cb && cli->cb->suspended) {
244 		cli->cb->suspended(cli);
245 	}
246 }
247 
blob_end(struct bt_mesh_blob_cli * b,const struct bt_mesh_blob_xfer * xfer,bool success)248 static void blob_end(struct bt_mesh_blob_cli *b,
249 		     const struct bt_mesh_blob_xfer *xfer, bool success)
250 {
251 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
252 
253 	cli->req.img_cb = NULL;
254 
255 	if (success) {
256 		refresh(cli);
257 		return;
258 	}
259 
260 	if (cli->xfer.state == STATE_CANCEL) {
261 		/* The user cancelled the transfer, DFU will end when all
262 		 * targets have been notified.
263 		 */
264 		return;
265 	}
266 
267 	if (cli->xfer.state != STATE_TRANSFER) {
268 		LOG_ERR("Blob failed in invalid state %u", cli->xfer.state);
269 		return;
270 	}
271 
272 	dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
273 }
274 
275 const struct bt_mesh_blob_cli_cb _bt_mesh_dfu_cli_blob_handlers = {
276 	.caps = blob_caps,
277 	.lost_target = blob_lost_target,
278 	.suspended = blob_suspended,
279 	.end = blob_end,
280 };
281 
282 /*******************************************************************************
283  * Message sending
284  ******************************************************************************/
285 
286 static void tx_start(uint16_t dur, int err, void *cb_data);
287 static void tx_end(int err, void *cb_data);
288 
289 static const struct bt_mesh_send_cb send_cb = {
290 	.start = tx_start,
291 	.end = tx_end,
292 };
293 
tx_start(uint16_t dur,int err,void * cb_data)294 static void tx_start(uint16_t dur, int err, void *cb_data)
295 {
296 	if (err) {
297 		tx_end(err, cb_data);
298 	}
299 }
300 
tx_end(int err,void * cb_data)301 static void tx_end(int err, void *cb_data)
302 {
303 	struct bt_mesh_dfu_cli *cli = cb_data;
304 
305 	blob_cli_broadcast_tx_complete(&cli->blob);
306 }
307 
tx(const struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf,const struct bt_mesh_send_cb * cb,struct bt_mesh_dfu_cli * cli)308 static int tx(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
309 	      struct net_buf_simple *buf, const struct bt_mesh_send_cb *cb,
310 	      struct bt_mesh_dfu_cli *cli)
311 {
312 	int err;
313 
314 	err = bt_mesh_model_send(mod, ctx, buf, cb, cli);
315 	if (err) {
316 		LOG_ERR("Send err: %d", err);
317 		if (cb) {
318 			cb->end(err, cli);
319 		}
320 		return err;
321 	}
322 
323 	return 0;
324 }
325 
info_get(struct bt_mesh_dfu_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t idx,uint8_t max_count,const struct bt_mesh_send_cb * cb)326 static int info_get(struct bt_mesh_dfu_cli *cli, struct bt_mesh_msg_ctx *ctx,
327 		    uint8_t idx, uint8_t max_count,
328 		    const struct bt_mesh_send_cb *cb)
329 {
330 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_INFO_GET, 2);
331 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_INFO_GET);
332 	net_buf_simple_add_u8(&buf, idx);
333 	net_buf_simple_add_u8(&buf, max_count);
334 
335 	return tx(cli->mod, ctx, &buf, cb, cli);
336 }
337 
send_info_get(struct bt_mesh_blob_cli * b,uint16_t dst)338 static void send_info_get(struct bt_mesh_blob_cli *b, uint16_t dst)
339 {
340 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
341 	struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst);
342 
343 	cli->req.img_cnt = 0xff;
344 
345 	info_get(cli, &ctx, 0, cli->req.img_cnt, &send_cb);
346 }
347 
send_update_start(struct bt_mesh_blob_cli * b,uint16_t dst)348 static void send_update_start(struct bt_mesh_blob_cli *b, uint16_t dst)
349 {
350 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
351 	struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst);
352 	struct bt_mesh_dfu_target *target;
353 
354 	if (b->tx.ctx.force_unicast) {
355 		target = target_get(cli, dst);
356 	} else {
357 		target = SYS_SLIST_PEEK_HEAD_CONTAINER(
358 						(sys_slist_t *)&((cli)->blob.inputs)->targets,
359 						target, blob.n);
360 	}
361 
362 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_START,
363 				 DFU_UPDATE_START_MSG_MAXLEN);
364 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_START);
365 
366 	net_buf_simple_add_u8(&buf, cli->blob.inputs->ttl);
367 	net_buf_simple_add_le16(&buf, cli->blob.inputs->timeout_base);
368 	net_buf_simple_add_le64(&buf, cli->xfer.blob.id);
369 	net_buf_simple_add_u8(&buf, target->img_idx);
370 	net_buf_simple_add_mem(&buf, cli->xfer.slot->metadata,
371 			       cli->xfer.slot->metadata_len);
372 
373 	(void)tx(cli->mod, &ctx, &buf, &send_cb, cli);
374 }
375 
send_update_get(struct bt_mesh_blob_cli * b,uint16_t dst)376 static void send_update_get(struct bt_mesh_blob_cli *b, uint16_t dst)
377 {
378 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
379 	struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst);
380 
381 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_GET, 0);
382 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_GET);
383 
384 	(void)tx(cli->mod, &ctx, &buf, &send_cb, cli);
385 }
386 
send_update_cancel(struct bt_mesh_blob_cli * b,uint16_t dst)387 static void send_update_cancel(struct bt_mesh_blob_cli *b, uint16_t dst)
388 {
389 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
390 	struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst);
391 
392 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_CANCEL, 0);
393 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_CANCEL);
394 
395 	(void)tx(cli->mod, &ctx, &buf, &send_cb, cli);
396 }
397 
send_update_apply(struct bt_mesh_blob_cli * b,uint16_t dst)398 static void send_update_apply(struct bt_mesh_blob_cli *b, uint16_t dst)
399 {
400 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
401 	struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst);
402 
403 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_APPLY, 0);
404 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_APPLY);
405 
406 	(void)tx(cli->mod, &ctx, &buf, &send_cb, cli);
407 }
408 
409 /*******************************************************************************
410  * Distribution procedure
411  ******************************************************************************/
412 static void transfer(struct bt_mesh_blob_cli *b);
413 static void apply(struct bt_mesh_dfu_cli *cli);
414 static void applied(struct bt_mesh_blob_cli *b);
415 static void confirmed(struct bt_mesh_blob_cli *b);
416 static void cancelled(struct bt_mesh_blob_cli *b);
417 
initiate(struct bt_mesh_dfu_cli * cli)418 static void initiate(struct bt_mesh_dfu_cli *cli)
419 {
420 	struct blob_cli_broadcast_ctx tx = {
421 		.send = send_update_start,
422 		.next = transfer,
423 		.acked = true,
424 	};
425 	struct bt_mesh_dfu_target *target;
426 	int img_idx = -1;
427 
428 	/** If firmware img index is the same for all targets, we can send Firmware Update Start
429 	 * message using multicast address. Otherwise, it has to be send in a unicast way.
430 	 */
431 	TARGETS_FOR_EACH(cli, target) {
432 		if (img_idx == -1) {
433 			img_idx = target->img_idx;
434 		} else if (target->img_idx != img_idx) {
435 			tx.force_unicast = true;
436 			break;
437 		}
438 	}
439 
440 	LOG_DBG("");
441 
442 	cli->op = BT_MESH_DFU_OP_UPDATE_STATUS;
443 	cli->xfer.state = STATE_TRANSFER;
444 
445 	blob_cli_broadcast(&cli->blob, &tx);
446 }
447 
skip_targets_from_broadcast(struct bt_mesh_dfu_cli * cli,bool skip)448 static void skip_targets_from_broadcast(struct bt_mesh_dfu_cli *cli, bool skip)
449 {
450 	struct bt_mesh_dfu_target *target;
451 
452 	TARGETS_FOR_EACH(cli, target) {
453 		/* If distributor is in the targets list, or target is in Verify phase,
454 		 * disable it until Retrieve Capabilities and BLOB Transfer procedures
455 		 * are completed.
456 		 */
457 		if (bt_mesh_has_addr(target->blob.addr) ||
458 		    target->phase == BT_MESH_DFU_PHASE_VERIFY) {
459 			target->blob.skip = skip;
460 			break;
461 		}
462 	}
463 }
464 
transfer_skip(struct bt_mesh_dfu_cli * cli)465 static bool transfer_skip(struct bt_mesh_dfu_cli *cli)
466 {
467 	struct bt_mesh_dfu_target *target;
468 
469 	TARGETS_FOR_EACH(cli, target) {
470 		if (!bt_mesh_has_addr(target->blob.addr) || !target->blob.skip) {
471 			return false;
472 		}
473 	}
474 
475 	return true;
476 }
477 
transfer(struct bt_mesh_blob_cli * b)478 static void transfer(struct bt_mesh_blob_cli *b)
479 {
480 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
481 	int err;
482 
483 	LOG_DBG("");
484 
485 	if (!targets_active(cli)) {
486 		dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
487 		return;
488 	}
489 
490 	skip_targets_from_broadcast(cli, true);
491 
492 	if (transfer_skip(cli)) {
493 		/* If distributor only updates itself, or all targets are in Verify phase,
494 		 * proceed to the refresh step immediately.
495 		 */
496 		refresh(cli);
497 		return;
498 	}
499 
500 	if (cli->xfer.flags & FLAG_RESUME) {
501 		cli->xfer.flags ^= FLAG_RESUME;
502 		err = bt_mesh_blob_cli_resume(b);
503 		if (err) {
504 			LOG_ERR("Resuming BLOB xfer failed: %d", err);
505 			dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY);
506 		}
507 	} else if (cli->xfer.flags & FLAG_SKIP_CAPS_GET) {
508 		cli->xfer.flags ^= FLAG_SKIP_CAPS_GET;
509 		err = bt_mesh_blob_cli_send(b, b->inputs, &cli->xfer.blob, cli->xfer.io);
510 		if (err) {
511 			LOG_ERR("Starting BLOB xfer failed: %d", err);
512 			dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY);
513 		}
514 	} else {
515 		err = bt_mesh_blob_cli_caps_get(&cli->blob, cli->blob.inputs);
516 		if (err) {
517 			LOG_ERR("Failed starting blob xfer: %d", err);
518 			dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY);
519 		}
520 	}
521 }
522 
refreshed(struct bt_mesh_blob_cli * b)523 static void refreshed(struct bt_mesh_blob_cli *b)
524 {
525 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
526 
527 	if (!targets_active(cli)) {
528 		dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
529 		return;
530 	}
531 
532 	cli->xfer.state = STATE_VERIFIED;
533 	dfu_complete(cli);
534 }
535 
refresh(struct bt_mesh_dfu_cli * cli)536 static void refresh(struct bt_mesh_dfu_cli *cli)
537 {
538 	const struct blob_cli_broadcast_ctx tx = {
539 		.send = send_update_get,
540 		.next = refreshed,
541 		.acked = true
542 	};
543 
544 	LOG_DBG("");
545 
546 	cli->xfer.state = STATE_REFRESH;
547 	cli->op = BT_MESH_DFU_OP_UPDATE_STATUS;
548 
549 	/* If distributor is in the targets list, enable it again so it participates in Distribute
550 	 * Firmware procedure.
551 	 */
552 	skip_targets_from_broadcast(cli, false);
553 
554 	blob_cli_broadcast(&cli->blob, &tx);
555 }
556 
apply(struct bt_mesh_dfu_cli * cli)557 static void apply(struct bt_mesh_dfu_cli *cli)
558 {
559 	const struct blob_cli_broadcast_ctx tx = {
560 		.send = send_update_apply,
561 		.next = applied,
562 		.acked = true
563 	};
564 
565 	LOG_DBG("");
566 
567 	cli->xfer.state = STATE_APPLY;
568 	cli->op = BT_MESH_DFU_OP_UPDATE_STATUS;
569 
570 	blob_cli_broadcast(&cli->blob, &tx);
571 }
572 
applied(struct bt_mesh_blob_cli * b)573 static void applied(struct bt_mesh_blob_cli *b)
574 {
575 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
576 
577 	if (!targets_active(cli)) {
578 		dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
579 		return;
580 	}
581 
582 	dfu_applied(cli);
583 }
584 
target_img_cb(struct bt_mesh_dfu_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t idx,uint8_t cnt,const struct bt_mesh_dfu_img * img,void * cb_data)585 static enum bt_mesh_dfu_iter target_img_cb(struct bt_mesh_dfu_cli *cli,
586 					   struct bt_mesh_msg_ctx *ctx,
587 					   uint8_t idx, uint8_t cnt,
588 					   const struct bt_mesh_dfu_img *img,
589 					   void *cb_data)
590 {
591 	struct bt_mesh_dfu_target *target;
592 
593 	if ((img->fwid_len != cli->xfer.slot->fwid_len) ||
594 	    memcmp(cli->xfer.slot->fwid, img->fwid, img->fwid_len)) {
595 		return BT_MESH_DFU_ITER_CONTINUE;
596 	}
597 
598 	target = target_get(cli, ctx->addr);
599 	if (target) {
600 		LOG_DBG("SUCCESS: 0x%04x applied dfu (as image %u)", ctx->addr,
601 			idx);
602 		target->phase = BT_MESH_DFU_PHASE_APPLY_SUCCESS;
603 		blob_cli_broadcast_rsp(&cli->blob, &target->blob);
604 	} else {
605 		LOG_WRN("Target 0x%04x not found", ctx->addr);
606 	}
607 
608 	return BT_MESH_DFU_ITER_STOP;
609 }
610 
confirm(struct bt_mesh_dfu_cli * cli)611 static void confirm(struct bt_mesh_dfu_cli *cli)
612 {
613 	const struct blob_cli_broadcast_ctx tx = {
614 		.send = send_info_get,
615 		.next = confirmed,
616 		.acked = true,
617 		.optional = true,
618 	};
619 
620 	LOG_DBG("");
621 
622 	cli->op = BT_MESH_DFU_OP_UPDATE_INFO_STATUS;
623 	cli->req.img_cb = target_img_cb;
624 	cli->req.ttl = cli->blob.inputs->ttl;
625 
626 	blob_cli_broadcast(&cli->blob, &tx);
627 }
628 
confirmed(struct bt_mesh_blob_cli * b)629 static void confirmed(struct bt_mesh_blob_cli *b)
630 {
631 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
632 	struct bt_mesh_dfu_target *target;
633 	bool success = false;
634 
635 	cli->req.img_cb = NULL;
636 
637 	TARGETS_FOR_EACH(cli, target) {
638 		if (target->status != BT_MESH_DFU_SUCCESS) {
639 			/* Target either failed at earlier stage or during confirmation. In any
640 			 * case, the app is already notified. Don't consider the target here.
641 			 */
642 			continue;
643 		}
644 
645 		if (target->effect == BT_MESH_DFU_EFFECT_UNPROV) {
646 			if (!target->blob.acked) {
647 				success = true;
648 				continue;
649 			}
650 
651 			LOG_DBG("Target 0x%04x still provisioned", target->blob.addr);
652 			target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL;
653 			target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL);
654 		} else if (!target->blob.acked) {
655 			LOG_DBG("Target 0x%04x failed to respond", target->blob.addr);
656 			target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL;
657 			target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL);
658 		} else if (target->status == BT_MESH_DFU_SUCCESS) {
659 			success = true;
660 		}
661 	}
662 
663 	if (success) {
664 		cli->xfer.state = STATE_IDLE;
665 		cli->xfer.flags = FLAG_COMPLETED;
666 
667 		if (cli->cb && cli->cb->confirmed) {
668 			cli->cb->confirmed(cli);
669 		}
670 	} else {
671 		dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
672 	}
673 }
674 
cancel(struct bt_mesh_dfu_cli * cli)675 static void cancel(struct bt_mesh_dfu_cli *cli)
676 {
677 	const struct blob_cli_broadcast_ctx tx = {
678 		.send = send_update_cancel,
679 		.next = cancelled,
680 		.acked = true
681 	};
682 
683 	LOG_DBG("");
684 
685 	cli->op = BT_MESH_DFU_OP_UPDATE_STATUS;
686 
687 	blob_cli_broadcast(&cli->blob, &tx);
688 }
689 
cancelled(struct bt_mesh_blob_cli * b)690 static void cancelled(struct bt_mesh_blob_cli *b)
691 {
692 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
693 
694 	cli->xfer.flags |= FLAG_CANCELLED;
695 	dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
696 }
697 
698 /*******************************************************************************
699  * Message handlers
700  ******************************************************************************/
701 
handle_status(const struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)702 static int handle_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
703 			 struct net_buf_simple *buf)
704 {
705 	struct bt_mesh_dfu_cli *cli = mod->rt->user_data;
706 	enum bt_mesh_dfu_status status;
707 	enum bt_mesh_dfu_phase phase;
708 	struct bt_mesh_dfu_target *target;
709 	uint8_t byte;
710 
711 	byte = net_buf_simple_pull_u8(buf);
712 	status = byte & BIT_MASK(3);
713 	phase = byte >> 5;
714 
715 	if (cli->req.type == REQ_STATUS && cli->req.addr == ctx->addr) {
716 		if (cli->req.params) {
717 			struct bt_mesh_dfu_target_status *rsp = cli->req.params;
718 
719 			rsp->status = status;
720 			rsp->phase = phase;
721 			if (buf->len == 13) {
722 				rsp->ttl = net_buf_simple_pull_u8(buf);
723 				rsp->effect = net_buf_simple_pull_u8(buf) & BIT_MASK(5);
724 				rsp->timeout_base = net_buf_simple_pull_le16(buf);
725 				rsp->blob_id = net_buf_simple_pull_le64(buf);
726 				rsp->img_idx = net_buf_simple_pull_u8(buf);
727 			} else if (buf->len) {
728 				return -EINVAL;
729 			}
730 
731 			rsp->ttl = 0U;
732 			rsp->effect = BT_MESH_DFU_EFFECT_NONE;
733 			rsp->timeout_base = 0U;
734 			rsp->blob_id = 0U;
735 			rsp->img_idx = 0U;
736 		}
737 		k_sem_give(&cli->req.sem);
738 	}
739 	if (cli->op != BT_MESH_DFU_OP_UPDATE_STATUS) {
740 		return 0;
741 	}
742 
743 	target = target_get(cli, ctx->addr);
744 	if (!target) {
745 		LOG_WRN("Unknown target 0x%04x", ctx->addr);
746 		return -ENOENT;
747 	}
748 
749 	LOG_DBG("%u phase: %u, cur state: %u", status, phase, cli->xfer.state);
750 
751 	target->phase = phase;
752 
753 	if (cli->xfer.state == STATE_APPLY && phase == BT_MESH_DFU_PHASE_IDLE &&
754 	    status == BT_MESH_DFU_ERR_WRONG_PHASE) {
755 		LOG_DBG("Response received with Idle phase");
756 		blob_cli_broadcast_rsp(&cli->blob, &target->blob);
757 		return 0;
758 	}
759 
760 	if (status != BT_MESH_DFU_SUCCESS) {
761 		target_failed(cli, target, status);
762 		blob_cli_broadcast_rsp(&cli->blob, &target->blob);
763 		return 0;
764 	}
765 
766 	if (buf->len == 13) {
767 		net_buf_simple_pull_u8(buf); /* ttl */
768 		target->effect = net_buf_simple_pull_u8(buf) & BIT_MASK(5);
769 		net_buf_simple_pull_le16(buf); /* timeout */
770 
771 		if (net_buf_simple_pull_le64(buf) != cli->xfer.blob.id) {
772 			LOG_WRN("Invalid BLOB ID");
773 			target_failed(cli, target, BT_MESH_DFU_ERR_BLOB_XFER_BUSY);
774 			blob_cli_broadcast_rsp(&cli->blob, &target->blob);
775 			return 0;
776 		}
777 
778 		target->img_idx = net_buf_simple_pull_u8(buf);
779 
780 		LOG_DBG("Target 0x%04x receiving transfer", ctx->addr);
781 	} else if (buf->len) {
782 		return -EINVAL;
783 	}
784 
785 	if (cli->xfer.state == STATE_REFRESH) {
786 		if (phase == BT_MESH_DFU_PHASE_VERIFY) {
787 			LOG_DBG("Still pending...");
788 			return 0;
789 		} else if (phase == BT_MESH_DFU_PHASE_VERIFY_FAIL) {
790 			LOG_WRN("Verification failed on target 0x%04x",
791 				target->blob.addr);
792 			target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE);
793 		}
794 	} else if (cli->xfer.state == STATE_APPLY) {
795 		if (phase != BT_MESH_DFU_PHASE_APPLYING &&
796 		    (target->effect == BT_MESH_DFU_EFFECT_UNPROV ||
797 		     phase != BT_MESH_DFU_PHASE_IDLE)) {
798 			LOG_WRN("Target 0x%04x in phase %u after apply",
799 				target->blob.addr, phase);
800 			target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE);
801 			blob_cli_broadcast_rsp(&cli->blob, &target->blob);
802 			return 0;
803 		}
804 		return 0;
805 	} else if (cli->xfer.state == STATE_CONFIRM) {
806 		if (phase == BT_MESH_DFU_PHASE_APPLYING) {
807 			LOG_DBG("Still pending...");
808 			return 0;
809 		}
810 
811 		if (phase != BT_MESH_DFU_PHASE_IDLE) {
812 			LOG_WRN("Target 0x%04x in phase %u after apply",
813 				target->blob.addr, phase);
814 			target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL;
815 			target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE);
816 			blob_cli_broadcast_rsp(&cli->blob, &target->blob);
817 			return 0;
818 		}
819 	} else if (cli->xfer.state == STATE_CANCEL) {
820 		target->phase = BT_MESH_DFU_PHASE_TRANSFER_CANCELED;
821 	}
822 
823 	blob_cli_broadcast_rsp(&cli->blob, &target->blob);
824 
825 	return 0;
826 }
827 
handle_info_status(const struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)828 static int handle_info_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
829 			      struct net_buf_simple *buf)
830 {
831 	struct bt_mesh_dfu_cli *cli = mod->rt->user_data;
832 	struct bt_mesh_dfu_target *target;
833 	enum bt_mesh_dfu_iter it = BT_MESH_DFU_ITER_CONTINUE;
834 	uint8_t img_cnt, idx;
835 
836 	if (!cli->req.img_cb ||
837 	    (cli->req.type == REQ_IMG && cli->req.addr != ctx->addr)) {
838 		LOG_WRN("Unexpected info status from 0x%04x", ctx->addr);
839 		return 0;
840 	}
841 
842 	img_cnt = net_buf_simple_pull_u8(buf);
843 	if (img_cnt < cli->req.img_cnt) {
844 		cli->req.img_cnt = img_cnt;
845 	}
846 
847 	idx = net_buf_simple_pull_u8(buf);
848 	if (idx >= img_cnt) {
849 		LOG_WRN("Invalid idx %u", idx);
850 		return -ENOENT;
851 	}
852 
853 	LOG_DBG("Image list from 0x%04x from index %u", ctx->addr, idx);
854 
855 	while (buf->len && cli->req.img_cb && idx < cli->req.img_cnt) {
856 		char uri_buf[CONFIG_BT_MESH_DFU_URI_MAXLEN + 1];
857 		struct bt_mesh_dfu_img img;
858 		size_t uri_len;
859 
860 		img.fwid_len = net_buf_simple_pull_u8(buf);
861 		if (buf->len < img.fwid_len + 1) {
862 			LOG_WRN("Invalid format: fwid");
863 			return -EINVAL;
864 		}
865 
866 		img.fwid = net_buf_simple_pull_mem(buf, img.fwid_len);
867 
868 		uri_len = net_buf_simple_pull_u8(buf);
869 		if (buf->len < uri_len) {
870 			LOG_WRN("Invalid format: uri");
871 			return -EINVAL;
872 		}
873 
874 		LOG_DBG("\tImage %u\n\r\tfwid: %s", idx, bt_hex(img.fwid, img.fwid_len));
875 
876 		if (uri_len) {
877 			size_t uri_buf_len =
878 				MIN(CONFIG_BT_MESH_DFU_URI_MAXLEN, uri_len);
879 
880 			memcpy(uri_buf, net_buf_simple_pull_mem(buf, uri_len),
881 			       uri_buf_len);
882 			uri_buf[uri_buf_len] = '\0';
883 			img.uri = uri_buf;
884 		} else {
885 			img.uri = NULL;
886 		}
887 
888 		it = cli->req.img_cb(cli, ctx, idx, img_cnt, &img,
889 				     cli->req.params);
890 		if (it != BT_MESH_DFU_ITER_CONTINUE) {
891 			if (cli->req.type == REQ_IMG) {
892 				k_sem_give(&cli->req.sem);
893 			}
894 
895 			return 0;
896 		}
897 
898 		idx++;
899 	}
900 
901 	if (idx < cli->req.img_cnt) {
902 		LOG_DBG("Fetching more images (%u/%u)", idx, cli->req.img_cnt);
903 		ctx->send_ttl = cli->req.ttl;
904 		info_get(cli, ctx, idx, cli->req.img_cnt - idx,
905 			 (cli->req.type == REQ_IMG) ? NULL : &send_cb);
906 		return 0;
907 	}
908 
909 	if (cli->req.type == REQ_IMG) {
910 		k_sem_give(&cli->req.sem);
911 		return 0;
912 	}
913 
914 	/* Confirm-procedure termination: */
915 	target = target_get(cli, ctx->addr);
916 	if (target) {
917 		LOG_WRN("Target 0x%04x failed to apply image: %s", ctx->addr,
918 			bt_hex(cli->xfer.slot->fwid, cli->xfer.slot->fwid_len));
919 		target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL;
920 		target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL);
921 		blob_cli_broadcast_rsp(&cli->blob, &target->blob);
922 	}
923 
924 	return 0;
925 }
926 
handle_metadata_status(const struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)927 static int handle_metadata_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
928 				  struct net_buf_simple *buf)
929 {
930 	struct bt_mesh_dfu_cli *cli = mod->rt->user_data;
931 	struct bt_mesh_dfu_metadata_status *rsp = cli->req.params;
932 	uint8_t hdr, idx;
933 
934 	hdr = net_buf_simple_pull_u8(buf);
935 	idx = net_buf_simple_pull_u8(buf);
936 
937 	if (cli->req.type != REQ_METADATA || ctx->addr != cli->req.addr ||
938 	    idx != rsp->idx) {
939 		LOG_WRN("Unexpected metadata status from 0x%04x img %u",
940 			ctx->addr, idx);
941 		if (cli->req.type != REQ_METADATA) {
942 			LOG_WRN("Expected %u", cli->req.type);
943 		} else {
944 			LOG_WRN("Expected 0x%04x img %u", cli->req.addr, idx);
945 		}
946 
947 		return 0;
948 	}
949 
950 	rsp->status = hdr & BIT_MASK(3);
951 	rsp->effect = (hdr >> 3);
952 	k_sem_give(&cli->req.sem);
953 
954 	return 0;
955 }
956 
957 const struct bt_mesh_model_op _bt_mesh_dfu_cli_op[] = {
958 	{BT_MESH_DFU_OP_UPDATE_STATUS, BT_MESH_LEN_MIN(1), handle_status},
959 	{BT_MESH_DFU_OP_UPDATE_INFO_STATUS, BT_MESH_LEN_MIN(2), handle_info_status},
960 	{BT_MESH_DFU_OP_UPDATE_METADATA_STATUS, BT_MESH_LEN_EXACT(2), handle_metadata_status},
961 	BT_MESH_MODEL_OP_END,
962 };
963 
dfu_cli_init(const struct bt_mesh_model * mod)964 static int dfu_cli_init(const struct bt_mesh_model *mod)
965 {
966 	struct bt_mesh_dfu_cli *cli = mod->rt->user_data;
967 
968 	if (mod->rt->elem_idx != 0) {
969 		LOG_ERR("DFU update client must be instantiated on first elem");
970 		return -EINVAL;
971 	}
972 
973 	cli->mod = mod;
974 
975 	if (IS_ENABLED(CONFIG_BT_MESH_MODEL_EXTENSIONS)) {
976 		bt_mesh_model_extend(mod, cli->blob.mod);
977 	}
978 
979 	k_sem_init(&cli->req.sem, 0, 1);
980 
981 	return 0;
982 }
983 
dfu_cli_reset(const struct bt_mesh_model * mod)984 static void dfu_cli_reset(const struct bt_mesh_model *mod)
985 {
986 	struct bt_mesh_dfu_cli *cli = mod->rt->user_data;
987 
988 	cli->req.type = REQ_NONE;
989 	cli->req.addr = BT_MESH_ADDR_UNASSIGNED;
990 	cli->req.img_cnt = 0;
991 	cli->req.img_cb = NULL;
992 	cli->xfer.state = STATE_IDLE;
993 	cli->xfer.flags = 0;
994 }
995 
996 const struct bt_mesh_model_cb _bt_mesh_dfu_cli_cb = {
997 	.init = dfu_cli_init,
998 	.reset = dfu_cli_reset,
999 };
1000 
1001 /*******************************************************************************
1002  * Public API
1003  ******************************************************************************/
1004 
bt_mesh_dfu_cli_send(struct bt_mesh_dfu_cli * cli,const struct bt_mesh_blob_cli_inputs * inputs,const struct bt_mesh_blob_io * io,const struct bt_mesh_dfu_cli_xfer * xfer)1005 int bt_mesh_dfu_cli_send(struct bt_mesh_dfu_cli *cli,
1006 			 const struct bt_mesh_blob_cli_inputs *inputs,
1007 			 const struct bt_mesh_blob_io *io,
1008 			 const struct bt_mesh_dfu_cli_xfer *xfer)
1009 {
1010 	struct bt_mesh_dfu_target *target;
1011 
1012 	if (bt_mesh_dfu_cli_is_busy(cli)) {
1013 		return -EBUSY;
1014 	}
1015 
1016 	cli->xfer.blob.mode = xfer->mode;
1017 	cli->xfer.blob.size = xfer->slot->size;
1018 
1019 	if (xfer->blob_id == 0) {
1020 		sys_rand_get(&cli->xfer.blob.id, sizeof(cli->xfer.blob.id));
1021 	} else {
1022 		cli->xfer.blob.id = xfer->blob_id;
1023 	}
1024 
1025 	cli->xfer.io = io;
1026 	cli->blob.inputs = inputs;
1027 	cli->xfer.slot = xfer->slot;
1028 	cli->xfer.flags = 0U;
1029 
1030 	if (xfer->blob_params) {
1031 		cli->xfer.flags |= FLAG_SKIP_CAPS_GET;
1032 		cli->xfer.blob.block_size_log = xfer->blob_params->block_size_log;
1033 		cli->xfer.blob.chunk_size = xfer->blob_params->chunk_size;
1034 	}
1035 
1036 	/* Phase will be set based on target status messages: */
1037 	TARGETS_FOR_EACH(cli, target) {
1038 		target->status = BT_MESH_DFU_SUCCESS;
1039 		target->phase = BT_MESH_DFU_PHASE_UNKNOWN;
1040 	}
1041 
1042 	initiate(cli);
1043 	return 0;
1044 }
1045 
bt_mesh_dfu_cli_suspend(struct bt_mesh_dfu_cli * cli)1046 int bt_mesh_dfu_cli_suspend(struct bt_mesh_dfu_cli *cli)
1047 {
1048 	int err;
1049 
1050 	err = bt_mesh_blob_cli_suspend(&cli->blob);
1051 	if (!err) {
1052 		cli->xfer.state = STATE_SUSPENDED;
1053 	}
1054 
1055 	return err;
1056 }
1057 
bt_mesh_dfu_cli_resume(struct bt_mesh_dfu_cli * cli)1058 int bt_mesh_dfu_cli_resume(struct bt_mesh_dfu_cli *cli)
1059 {
1060 	struct bt_mesh_dfu_target *target;
1061 
1062 	if (cli->xfer.state != STATE_SUSPENDED) {
1063 		return -EINVAL;
1064 	}
1065 
1066 	cli->xfer.flags = FLAG_RESUME;
1067 
1068 	/* Restore timed out targets. */
1069 	TARGETS_FOR_EACH(cli, target) {
1070 		if (!!target->blob.timedout) {
1071 			target->status = BT_MESH_DFU_SUCCESS;
1072 			target->phase = BT_MESH_DFU_PHASE_UNKNOWN;
1073 		}
1074 	}
1075 
1076 	initiate(cli);
1077 	return 0;
1078 }
1079 
bt_mesh_dfu_cli_cancel(struct bt_mesh_dfu_cli * cli,struct bt_mesh_msg_ctx * ctx)1080 int bt_mesh_dfu_cli_cancel(struct bt_mesh_dfu_cli *cli,
1081 			   struct bt_mesh_msg_ctx *ctx)
1082 {
1083 	if (ctx) {
1084 		int err;
1085 
1086 		err = req_setup(cli, REQ_STATUS, ctx->addr, NULL);
1087 		if (err) {
1088 			return err;
1089 		}
1090 
1091 		BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_CANCEL, 0);
1092 		bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_CANCEL);
1093 
1094 		err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL);
1095 		if (err) {
1096 			cli->req.type = REQ_NONE;
1097 			return err;
1098 		}
1099 
1100 		return req_wait(cli, K_MSEC(dfu_cli_timeout));
1101 	}
1102 
1103 	if (cli->xfer.state == STATE_IDLE) {
1104 		return -EALREADY;
1105 	}
1106 
1107 	cli->xfer.state = STATE_CANCEL;
1108 	blob_cli_broadcast_abort(&cli->blob);
1109 	cancel(cli);
1110 	return 0;
1111 }
1112 
bt_mesh_dfu_cli_apply(struct bt_mesh_dfu_cli * cli)1113 int bt_mesh_dfu_cli_apply(struct bt_mesh_dfu_cli *cli)
1114 {
1115 	if (cli->xfer.state != STATE_VERIFIED) {
1116 		return -EBUSY;
1117 	}
1118 
1119 	apply(cli);
1120 
1121 	return 0;
1122 }
1123 
bt_mesh_dfu_cli_confirm(struct bt_mesh_dfu_cli * cli)1124 int bt_mesh_dfu_cli_confirm(struct bt_mesh_dfu_cli *cli)
1125 {
1126 	if (cli->xfer.state != STATE_APPLIED) {
1127 		return -EBUSY;
1128 	}
1129 
1130 	cli->xfer.state = STATE_CONFIRM;
1131 	confirm(cli);
1132 
1133 	return 0;
1134 }
1135 
bt_mesh_dfu_cli_progress(struct bt_mesh_dfu_cli * cli)1136 uint8_t bt_mesh_dfu_cli_progress(struct bt_mesh_dfu_cli *cli)
1137 {
1138 	if (cli->xfer.state == STATE_TRANSFER) {
1139 		return bt_mesh_blob_cli_xfer_progress_active_get(&cli->blob);
1140 	}
1141 
1142 	if (cli->xfer.state == STATE_IDLE) {
1143 		if (cli->xfer.flags & FLAG_COMPLETED) {
1144 			return 100U;
1145 		}
1146 		return 0U;
1147 	}
1148 
1149 	return 100U;
1150 }
1151 
bt_mesh_dfu_cli_is_busy(struct bt_mesh_dfu_cli * cli)1152 bool bt_mesh_dfu_cli_is_busy(struct bt_mesh_dfu_cli *cli)
1153 {
1154 	return (cli->xfer.state == STATE_TRANSFER ||
1155 		cli->xfer.state == STATE_REFRESH ||
1156 		cli->xfer.state == STATE_APPLY ||
1157 		cli->xfer.state == STATE_CONFIRM) &&
1158 	       !(cli->xfer.flags & FLAG_FAILED);
1159 }
1160 
bt_mesh_dfu_cli_imgs_get(struct bt_mesh_dfu_cli * cli,struct bt_mesh_msg_ctx * ctx,bt_mesh_dfu_img_cb_t cb,void * cb_data,uint8_t max_count)1161 int bt_mesh_dfu_cli_imgs_get(struct bt_mesh_dfu_cli *cli,
1162 			     struct bt_mesh_msg_ctx *ctx,
1163 			     bt_mesh_dfu_img_cb_t cb, void *cb_data,
1164 			     uint8_t max_count)
1165 {
1166 	int err;
1167 
1168 	if (cli->req.img_cb) {
1169 		return -EBUSY;
1170 	}
1171 
1172 	err = req_setup(cli, REQ_IMG, ctx->addr, NULL);
1173 	if (err) {
1174 		return err;
1175 	}
1176 
1177 	cli->req.img_cb = cb;
1178 	cli->req.params = cb_data;
1179 	cli->req.ttl = ctx->send_ttl;
1180 	cli->req.img_cnt = max_count;
1181 
1182 	err = info_get(cli, ctx, 0, cli->req.img_cnt,  NULL);
1183 	if (err) {
1184 		cli->req.img_cb = NULL;
1185 		cli->req.type = REQ_NONE;
1186 		return err;
1187 	}
1188 
1189 	err = req_wait(cli, K_MSEC(dfu_cli_timeout));
1190 
1191 	cli->req.img_cb = NULL;
1192 
1193 	return err;
1194 }
1195 
bt_mesh_dfu_cli_metadata_check(struct bt_mesh_dfu_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t img_idx,const struct bt_mesh_dfu_slot * slot,struct bt_mesh_dfu_metadata_status * rsp)1196 int bt_mesh_dfu_cli_metadata_check(struct bt_mesh_dfu_cli *cli,
1197 				   struct bt_mesh_msg_ctx *ctx, uint8_t img_idx,
1198 				   const struct bt_mesh_dfu_slot *slot,
1199 				   struct bt_mesh_dfu_metadata_status *rsp)
1200 {
1201 	int err;
1202 
1203 	err = req_setup(cli, REQ_METADATA, ctx->addr, rsp);
1204 	if (err) {
1205 		return err;
1206 	}
1207 
1208 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_METADATA_CHECK,
1209 				 1 + CONFIG_BT_MESH_DFU_METADATA_MAXLEN);
1210 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_METADATA_CHECK);
1211 
1212 	net_buf_simple_add_u8(&buf, img_idx);
1213 
1214 	if (slot->metadata_len) {
1215 		net_buf_simple_add_mem(&buf, slot->metadata,
1216 				       slot->metadata_len);
1217 	}
1218 
1219 	rsp->idx = img_idx;
1220 
1221 	err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL);
1222 	if (err) {
1223 		cli->req.type = REQ_NONE;
1224 		return err;
1225 	}
1226 
1227 	return req_wait(cli, K_MSEC(dfu_cli_timeout));
1228 }
1229 
bt_mesh_dfu_cli_status_get(struct bt_mesh_dfu_cli * cli,struct bt_mesh_msg_ctx * ctx,struct bt_mesh_dfu_target_status * rsp)1230 int bt_mesh_dfu_cli_status_get(struct bt_mesh_dfu_cli *cli,
1231 			       struct bt_mesh_msg_ctx *ctx,
1232 			       struct bt_mesh_dfu_target_status *rsp)
1233 {
1234 	int err;
1235 
1236 	err = req_setup(cli, REQ_STATUS, ctx->addr, rsp);
1237 	if (err) {
1238 		return err;
1239 	}
1240 
1241 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_GET, 0);
1242 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_GET);
1243 
1244 	err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL);
1245 	if (err) {
1246 		cli->req.type = REQ_NONE;
1247 		return err;
1248 	}
1249 
1250 	return req_wait(cli, K_MSEC(dfu_cli_timeout));
1251 }
1252 
bt_mesh_dfu_cli_timeout_get(void)1253 int32_t bt_mesh_dfu_cli_timeout_get(void)
1254 {
1255 	return dfu_cli_timeout;
1256 }
1257 
bt_mesh_dfu_cli_timeout_set(int32_t t)1258 void bt_mesh_dfu_cli_timeout_set(int32_t t)
1259 {
1260 	dfu_cli_timeout = t;
1261 }
1262