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 
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)308 static int info_get(struct bt_mesh_dfu_cli *cli, struct bt_mesh_msg_ctx *ctx,
309 		    uint8_t idx, uint8_t max_count,
310 		    const struct bt_mesh_send_cb *cb)
311 {
312 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_INFO_GET, 2);
313 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_INFO_GET);
314 	net_buf_simple_add_u8(&buf, idx);
315 	net_buf_simple_add_u8(&buf, max_count);
316 
317 	return bt_mesh_model_send(cli->mod, ctx, &buf, cb, cli);
318 }
319 
send_info_get(struct bt_mesh_blob_cli * b,uint16_t dst)320 static void send_info_get(struct bt_mesh_blob_cli *b, uint16_t dst)
321 {
322 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
323 	struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst);
324 
325 	cli->req.img_cnt = 0xff;
326 
327 	info_get(cli, &ctx, 0, cli->req.img_cnt, &send_cb);
328 }
329 
send_update_start(struct bt_mesh_blob_cli * b,uint16_t dst)330 static void send_update_start(struct bt_mesh_blob_cli *b, uint16_t dst)
331 {
332 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
333 	struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst);
334 	struct bt_mesh_dfu_target *target;
335 
336 	if (b->tx.ctx.force_unicast) {
337 		target = target_get(cli, dst);
338 	} else {
339 		target = SYS_SLIST_PEEK_HEAD_CONTAINER(
340 						(sys_slist_t *)&((cli)->blob.inputs)->targets,
341 						target, blob.n);
342 	}
343 
344 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_START,
345 				 DFU_UPDATE_START_MSG_MAXLEN);
346 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_START);
347 
348 	net_buf_simple_add_u8(&buf, cli->blob.inputs->ttl);
349 	net_buf_simple_add_le16(&buf, cli->blob.inputs->timeout_base);
350 	net_buf_simple_add_le64(&buf, cli->xfer.blob.id);
351 	net_buf_simple_add_u8(&buf, target->img_idx);
352 	net_buf_simple_add_mem(&buf, cli->xfer.slot->metadata,
353 			       cli->xfer.slot->metadata_len);
354 
355 	bt_mesh_model_send(cli->mod, &ctx, &buf, &send_cb, cli);
356 }
357 
send_update_get(struct bt_mesh_blob_cli * b,uint16_t dst)358 static void send_update_get(struct bt_mesh_blob_cli *b, uint16_t dst)
359 {
360 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
361 	struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst);
362 
363 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_GET, 0);
364 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_GET);
365 
366 	bt_mesh_model_send(cli->mod, &ctx, &buf, &send_cb, cli);
367 }
368 
send_update_cancel(struct bt_mesh_blob_cli * b,uint16_t dst)369 static void send_update_cancel(struct bt_mesh_blob_cli *b, uint16_t dst)
370 {
371 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
372 	struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst);
373 
374 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_CANCEL, 0);
375 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_CANCEL);
376 
377 	bt_mesh_model_send(cli->mod, &ctx, &buf, &send_cb, cli);
378 }
379 
send_update_apply(struct bt_mesh_blob_cli * b,uint16_t dst)380 static void send_update_apply(struct bt_mesh_blob_cli *b, uint16_t dst)
381 {
382 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
383 	struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst);
384 
385 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_APPLY, 0);
386 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_APPLY);
387 
388 	bt_mesh_model_send(cli->mod, &ctx, &buf, &send_cb, cli);
389 }
390 
391 /*******************************************************************************
392  * Distribution procedure
393  ******************************************************************************/
394 static void transfer(struct bt_mesh_blob_cli *b);
395 static void apply(struct bt_mesh_dfu_cli *cli);
396 static void applied(struct bt_mesh_blob_cli *b);
397 static void confirmed(struct bt_mesh_blob_cli *b);
398 static void cancelled(struct bt_mesh_blob_cli *b);
399 
initiate(struct bt_mesh_dfu_cli * cli)400 static void initiate(struct bt_mesh_dfu_cli *cli)
401 {
402 	struct blob_cli_broadcast_ctx tx = {
403 		.send = send_update_start,
404 		.next = transfer,
405 		.acked = true,
406 	};
407 	struct bt_mesh_dfu_target *target;
408 	int img_idx = -1;
409 
410 	/** If firmware img index is the same for all targets, we can send Firmware Update Start
411 	 * message using multicast address. Otherwise, it has to be send in a unicast way.
412 	 */
413 	TARGETS_FOR_EACH(cli, target) {
414 		if (img_idx == -1) {
415 			img_idx = target->img_idx;
416 		} else if (target->img_idx != img_idx) {
417 			tx.force_unicast = true;
418 			break;
419 		}
420 	}
421 
422 	LOG_DBG("");
423 
424 	cli->op = BT_MESH_DFU_OP_UPDATE_STATUS;
425 	cli->xfer.state = STATE_TRANSFER;
426 
427 	blob_cli_broadcast(&cli->blob, &tx);
428 }
429 
skip_targets_from_broadcast(struct bt_mesh_dfu_cli * cli,bool skip)430 static void skip_targets_from_broadcast(struct bt_mesh_dfu_cli *cli, bool skip)
431 {
432 	struct bt_mesh_dfu_target *target;
433 
434 	TARGETS_FOR_EACH(cli, target) {
435 		/* If distributor is in the targets list, or target is in Verify phase,
436 		 * disable it until Retrieve Capabilities and BLOB Transfer procedures
437 		 * are completed.
438 		 */
439 		if (bt_mesh_has_addr(target->blob.addr) ||
440 		    target->phase == BT_MESH_DFU_PHASE_VERIFY) {
441 			target->blob.skip = skip;
442 			break;
443 		}
444 	}
445 }
446 
transfer_skip(struct bt_mesh_dfu_cli * cli)447 static bool transfer_skip(struct bt_mesh_dfu_cli *cli)
448 {
449 	struct bt_mesh_dfu_target *target;
450 
451 	TARGETS_FOR_EACH(cli, target) {
452 		if (!bt_mesh_has_addr(target->blob.addr) || !target->blob.skip) {
453 			return false;
454 		}
455 	}
456 
457 	return true;
458 }
459 
transfer(struct bt_mesh_blob_cli * b)460 static void transfer(struct bt_mesh_blob_cli *b)
461 {
462 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
463 	int err;
464 
465 	LOG_DBG("");
466 
467 	if (!targets_active(cli)) {
468 		dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
469 		return;
470 	}
471 
472 	skip_targets_from_broadcast(cli, true);
473 
474 	if (transfer_skip(cli)) {
475 		/* If distributor only updates itself, or all targets are in Verify phase,
476 		 * proceed to the refresh step immediately.
477 		 */
478 		refresh(cli);
479 		return;
480 	}
481 
482 	if (cli->xfer.flags & FLAG_RESUME) {
483 		cli->xfer.flags ^= FLAG_RESUME;
484 		err = bt_mesh_blob_cli_resume(b);
485 		if (err) {
486 			LOG_ERR("Resuming BLOB xfer failed: %d", err);
487 			dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY);
488 		}
489 	} else if (cli->xfer.flags & FLAG_SKIP_CAPS_GET) {
490 		cli->xfer.flags ^= FLAG_SKIP_CAPS_GET;
491 		err = bt_mesh_blob_cli_send(b, b->inputs, &cli->xfer.blob, cli->xfer.io);
492 		if (err) {
493 			LOG_ERR("Starting BLOB xfer failed: %d", err);
494 			dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY);
495 		}
496 	} else {
497 		err = bt_mesh_blob_cli_caps_get(&cli->blob, cli->blob.inputs);
498 		if (err) {
499 			LOG_ERR("Failed starting blob xfer: %d", err);
500 			dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY);
501 		}
502 	}
503 }
504 
refreshed(struct bt_mesh_blob_cli * b)505 static void refreshed(struct bt_mesh_blob_cli *b)
506 {
507 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
508 
509 	if (!targets_active(cli)) {
510 		dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
511 		return;
512 	}
513 
514 	cli->xfer.state = STATE_VERIFIED;
515 	dfu_complete(cli);
516 }
517 
refresh(struct bt_mesh_dfu_cli * cli)518 static void refresh(struct bt_mesh_dfu_cli *cli)
519 {
520 	const struct blob_cli_broadcast_ctx tx = {
521 		.send = send_update_get,
522 		.next = refreshed,
523 		.acked = true
524 	};
525 
526 	LOG_DBG("");
527 
528 	cli->xfer.state = STATE_REFRESH;
529 	cli->op = BT_MESH_DFU_OP_UPDATE_STATUS;
530 
531 	/* If distributor is in the targets list, enable it again so it participates in Distribute
532 	 * Firmware procedure.
533 	 */
534 	skip_targets_from_broadcast(cli, false);
535 
536 	blob_cli_broadcast(&cli->blob, &tx);
537 }
538 
apply(struct bt_mesh_dfu_cli * cli)539 static void apply(struct bt_mesh_dfu_cli *cli)
540 {
541 	const struct blob_cli_broadcast_ctx tx = {
542 		.send = send_update_apply,
543 		.next = applied,
544 		.acked = true
545 	};
546 
547 	LOG_DBG("");
548 
549 	cli->xfer.state = STATE_APPLY;
550 	cli->op = BT_MESH_DFU_OP_UPDATE_STATUS;
551 
552 	blob_cli_broadcast(&cli->blob, &tx);
553 }
554 
applied(struct bt_mesh_blob_cli * b)555 static void applied(struct bt_mesh_blob_cli *b)
556 {
557 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
558 
559 	if (!targets_active(cli)) {
560 		dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
561 		return;
562 	}
563 
564 	dfu_applied(cli);
565 }
566 
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)567 static enum bt_mesh_dfu_iter target_img_cb(struct bt_mesh_dfu_cli *cli,
568 					   struct bt_mesh_msg_ctx *ctx,
569 					   uint8_t idx, uint8_t cnt,
570 					   const struct bt_mesh_dfu_img *img,
571 					   void *cb_data)
572 {
573 	struct bt_mesh_dfu_target *target;
574 
575 	if ((img->fwid_len != cli->xfer.slot->fwid_len) ||
576 	    memcmp(cli->xfer.slot->fwid, img->fwid, img->fwid_len)) {
577 		return BT_MESH_DFU_ITER_CONTINUE;
578 	}
579 
580 	target = target_get(cli, ctx->addr);
581 	if (target) {
582 		LOG_DBG("SUCCESS: 0x%04x applied dfu (as image %u)", ctx->addr,
583 			idx);
584 		target->phase = BT_MESH_DFU_PHASE_APPLY_SUCCESS;
585 		blob_cli_broadcast_rsp(&cli->blob, &target->blob);
586 	} else {
587 		LOG_WRN("Target 0x%04x not found", ctx->addr);
588 	}
589 
590 	return BT_MESH_DFU_ITER_STOP;
591 }
592 
confirm(struct bt_mesh_dfu_cli * cli)593 static void confirm(struct bt_mesh_dfu_cli *cli)
594 {
595 	const struct blob_cli_broadcast_ctx tx = {
596 		.send = send_info_get,
597 		.next = confirmed,
598 		.acked = true,
599 		.optional = true,
600 	};
601 
602 	LOG_DBG("");
603 
604 	cli->op = BT_MESH_DFU_OP_UPDATE_INFO_STATUS;
605 	cli->req.img_cb = target_img_cb;
606 	cli->req.ttl = cli->blob.inputs->ttl;
607 
608 	blob_cli_broadcast(&cli->blob, &tx);
609 }
610 
confirmed(struct bt_mesh_blob_cli * b)611 static void confirmed(struct bt_mesh_blob_cli *b)
612 {
613 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
614 	struct bt_mesh_dfu_target *target;
615 	bool success = false;
616 
617 	cli->req.img_cb = NULL;
618 
619 	TARGETS_FOR_EACH(cli, target) {
620 		if (target->status != BT_MESH_DFU_SUCCESS) {
621 			/* Target either failed at earlier stage or during confirmation. In any
622 			 * case, the app is already notified. Don't consider the target here.
623 			 */
624 			continue;
625 		}
626 
627 		if (target->effect == BT_MESH_DFU_EFFECT_UNPROV) {
628 			if (!target->blob.acked) {
629 				success = true;
630 				continue;
631 			}
632 
633 			LOG_DBG("Target 0x%04x still provisioned", target->blob.addr);
634 			target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL;
635 			target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL);
636 		} else if (!target->blob.acked) {
637 			LOG_DBG("Target 0x%04x failed to respond", target->blob.addr);
638 			target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL;
639 			target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL);
640 		} else if (target->status == BT_MESH_DFU_SUCCESS) {
641 			success = true;
642 		}
643 	}
644 
645 	if (success) {
646 		cli->xfer.state = STATE_IDLE;
647 		cli->xfer.flags = FLAG_COMPLETED;
648 
649 		if (cli->cb && cli->cb->confirmed) {
650 			cli->cb->confirmed(cli);
651 		}
652 	} else {
653 		dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
654 	}
655 }
656 
cancel(struct bt_mesh_dfu_cli * cli)657 static void cancel(struct bt_mesh_dfu_cli *cli)
658 {
659 	const struct blob_cli_broadcast_ctx tx = {
660 		.send = send_update_cancel,
661 		.next = cancelled,
662 		.acked = true
663 	};
664 
665 	LOG_DBG("");
666 
667 	cli->op = BT_MESH_DFU_OP_UPDATE_STATUS;
668 
669 	blob_cli_broadcast(&cli->blob, &tx);
670 }
671 
cancelled(struct bt_mesh_blob_cli * b)672 static void cancelled(struct bt_mesh_blob_cli *b)
673 {
674 	struct bt_mesh_dfu_cli *cli = DFU_CLI(b);
675 
676 	cli->xfer.flags |= FLAG_CANCELLED;
677 	dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL);
678 }
679 
680 /*******************************************************************************
681  * Message handlers
682  ******************************************************************************/
683 
handle_status(struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)684 static int handle_status(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
685 			 struct net_buf_simple *buf)
686 {
687 	struct bt_mesh_dfu_cli *cli = mod->user_data;
688 	enum bt_mesh_dfu_status status;
689 	enum bt_mesh_dfu_phase phase;
690 	struct bt_mesh_dfu_target *target;
691 	uint8_t byte;
692 
693 	byte = net_buf_simple_pull_u8(buf);
694 	status = byte & BIT_MASK(3);
695 	phase = byte >> 5;
696 
697 	if (cli->req.type == REQ_STATUS && cli->req.addr == ctx->addr) {
698 		if (cli->req.params) {
699 			struct bt_mesh_dfu_target_status *rsp = cli->req.params;
700 
701 			rsp->status = status;
702 			rsp->phase = phase;
703 			if (buf->len == 13) {
704 				rsp->ttl = net_buf_simple_pull_u8(buf);
705 				rsp->effect = net_buf_simple_pull_u8(buf) & BIT_MASK(5);
706 				rsp->timeout_base = net_buf_simple_pull_le16(buf);
707 				rsp->blob_id = net_buf_simple_pull_le64(buf);
708 				rsp->img_idx = net_buf_simple_pull_u8(buf);
709 			} else if (buf->len) {
710 				return -EINVAL;
711 			}
712 
713 			rsp->ttl = 0U;
714 			rsp->effect = BT_MESH_DFU_EFFECT_NONE;
715 			rsp->timeout_base = 0U;
716 			rsp->blob_id = 0U;
717 			rsp->img_idx = 0U;
718 		}
719 		k_sem_give(&cli->req.sem);
720 	}
721 	if (cli->op != BT_MESH_DFU_OP_UPDATE_STATUS) {
722 		return 0;
723 	}
724 
725 	target = target_get(cli, ctx->addr);
726 	if (!target) {
727 		LOG_WRN("Unknown target 0x%04x", ctx->addr);
728 		return -ENOENT;
729 	}
730 
731 	LOG_DBG("%u phase: %u, cur state: %u", status, phase, cli->xfer.state);
732 
733 	target->phase = phase;
734 
735 	if (cli->xfer.state == STATE_APPLY && phase == BT_MESH_DFU_PHASE_IDLE &&
736 	    status == BT_MESH_DFU_ERR_WRONG_PHASE) {
737 		LOG_DBG("Response received with Idle phase");
738 		blob_cli_broadcast_rsp(&cli->blob, &target->blob);
739 		return 0;
740 	}
741 
742 	if (status != BT_MESH_DFU_SUCCESS) {
743 		target_failed(cli, target, status);
744 		blob_cli_broadcast_rsp(&cli->blob, &target->blob);
745 		return 0;
746 	}
747 
748 	if (buf->len == 13) {
749 		net_buf_simple_pull_u8(buf); /* ttl */
750 		target->effect = net_buf_simple_pull_u8(buf) & BIT_MASK(5);
751 		net_buf_simple_pull_le16(buf); /* timeout */
752 
753 		if (net_buf_simple_pull_le64(buf) != cli->xfer.blob.id) {
754 			LOG_WRN("Invalid BLOB ID");
755 			target_failed(cli, target, BT_MESH_DFU_ERR_BLOB_XFER_BUSY);
756 			blob_cli_broadcast_rsp(&cli->blob, &target->blob);
757 			return 0;
758 		}
759 
760 		target->img_idx = net_buf_simple_pull_u8(buf);
761 
762 		LOG_DBG("Target 0x%04x receiving transfer", ctx->addr);
763 	} else if (buf->len) {
764 		return -EINVAL;
765 	}
766 
767 	if (cli->xfer.state == STATE_REFRESH) {
768 		if (phase == BT_MESH_DFU_PHASE_VERIFY) {
769 			LOG_DBG("Still pending...");
770 			return 0;
771 		} else if (phase == BT_MESH_DFU_PHASE_VERIFY_FAIL) {
772 			LOG_WRN("Verification failed on target 0x%04x",
773 				target->blob.addr);
774 			target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE);
775 		}
776 	} else if (cli->xfer.state == STATE_APPLY) {
777 		if (phase != BT_MESH_DFU_PHASE_APPLYING &&
778 		    (target->effect == BT_MESH_DFU_EFFECT_UNPROV ||
779 		     phase != BT_MESH_DFU_PHASE_IDLE)) {
780 			LOG_WRN("Target 0x%04x in phase %u after apply",
781 				target->blob.addr, phase);
782 			target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE);
783 			blob_cli_broadcast_rsp(&cli->blob, &target->blob);
784 			return 0;
785 		}
786 		return 0;
787 	} else if (cli->xfer.state == STATE_CONFIRM) {
788 		if (phase == BT_MESH_DFU_PHASE_APPLYING) {
789 			LOG_DBG("Still pending...");
790 			return 0;
791 		}
792 
793 		if (phase != BT_MESH_DFU_PHASE_IDLE) {
794 			LOG_WRN("Target 0x%04x in phase %u after apply",
795 				target->blob.addr, phase);
796 			target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL;
797 			target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE);
798 			blob_cli_broadcast_rsp(&cli->blob, &target->blob);
799 			return 0;
800 		}
801 	} else if (cli->xfer.state == STATE_CANCEL) {
802 		target->phase = BT_MESH_DFU_PHASE_TRANSFER_CANCELED;
803 	}
804 
805 	blob_cli_broadcast_rsp(&cli->blob, &target->blob);
806 
807 	return 0;
808 }
809 
handle_info_status(struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)810 static int handle_info_status(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
811 			      struct net_buf_simple *buf)
812 {
813 	struct bt_mesh_dfu_cli *cli = mod->user_data;
814 	struct bt_mesh_dfu_target *target;
815 	enum bt_mesh_dfu_iter it = BT_MESH_DFU_ITER_CONTINUE;
816 	uint8_t img_cnt, idx;
817 
818 	if (!cli->req.img_cb ||
819 	    (cli->req.type == REQ_IMG && cli->req.addr != ctx->addr)) {
820 		LOG_WRN("Unexpected info status from 0x%04x", ctx->addr);
821 		return 0;
822 	}
823 
824 	img_cnt = net_buf_simple_pull_u8(buf);
825 	if (img_cnt < cli->req.img_cnt) {
826 		cli->req.img_cnt = img_cnt;
827 	}
828 
829 	idx = net_buf_simple_pull_u8(buf);
830 	if (idx >= img_cnt) {
831 		LOG_WRN("Invalid idx %u", idx);
832 		return -ENOENT;
833 	}
834 
835 	LOG_DBG("Image list from 0x%04x from index %u", ctx->addr, idx);
836 
837 	while (buf->len && cli->req.img_cb && idx < cli->req.img_cnt) {
838 		char uri_buf[CONFIG_BT_MESH_DFU_URI_MAXLEN + 1];
839 		struct bt_mesh_dfu_img img;
840 		size_t uri_len;
841 
842 		img.fwid_len = net_buf_simple_pull_u8(buf);
843 		if (buf->len < img.fwid_len + 1) {
844 			LOG_WRN("Invalid format: fwid");
845 			return -EINVAL;
846 		}
847 
848 		img.fwid = net_buf_simple_pull_mem(buf, img.fwid_len);
849 
850 		uri_len = net_buf_simple_pull_u8(buf);
851 		if (buf->len < uri_len) {
852 			LOG_WRN("Invalid format: uri");
853 			return -EINVAL;
854 		}
855 
856 		LOG_DBG("\tImage %u\n\r\tfwid: %s", idx, bt_hex(img.fwid, img.fwid_len));
857 
858 		if (uri_len) {
859 			size_t uri_buf_len =
860 				MIN(CONFIG_BT_MESH_DFU_URI_MAXLEN, uri_len);
861 
862 			memcpy(uri_buf, net_buf_simple_pull_mem(buf, uri_len),
863 			       uri_buf_len);
864 			uri_buf[uri_buf_len] = '\0';
865 			img.uri = uri_buf;
866 		} else {
867 			img.uri = NULL;
868 		}
869 
870 		it = cli->req.img_cb(cli, ctx, idx, img_cnt, &img,
871 				     cli->req.params);
872 		if (it != BT_MESH_DFU_ITER_CONTINUE) {
873 			if (cli->req.type == REQ_IMG) {
874 				k_sem_give(&cli->req.sem);
875 			}
876 
877 			return 0;
878 		}
879 
880 		idx++;
881 	}
882 
883 	if (idx < cli->req.img_cnt) {
884 		LOG_DBG("Fetching more images (%u/%u)", idx, cli->req.img_cnt);
885 		ctx->send_ttl = cli->req.ttl;
886 		info_get(cli, ctx, idx, cli->req.img_cnt - idx,
887 			 (cli->req.type == REQ_IMG) ? NULL : &send_cb);
888 		return 0;
889 	}
890 
891 	if (cli->req.type == REQ_IMG) {
892 		k_sem_give(&cli->req.sem);
893 		return 0;
894 	}
895 
896 	/* Confirm-procedure termination: */
897 	target = target_get(cli, ctx->addr);
898 	if (target) {
899 		LOG_WRN("Target 0x%04x failed to apply image: %s", ctx->addr,
900 			bt_hex(cli->xfer.slot->fwid, cli->xfer.slot->fwid_len));
901 		target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL;
902 		target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL);
903 		blob_cli_broadcast_rsp(&cli->blob, &target->blob);
904 	}
905 
906 	return 0;
907 }
908 
handle_metadata_status(struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)909 static int handle_metadata_status(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
910 				  struct net_buf_simple *buf)
911 {
912 	struct bt_mesh_dfu_cli *cli = mod->user_data;
913 	struct bt_mesh_dfu_metadata_status *rsp = cli->req.params;
914 	uint8_t hdr, idx;
915 
916 	hdr = net_buf_simple_pull_u8(buf);
917 	idx = net_buf_simple_pull_u8(buf);
918 
919 	if (cli->req.type != REQ_METADATA || ctx->addr != cli->req.addr ||
920 	    idx != rsp->idx) {
921 		LOG_WRN("Unexpected metadata status from 0x%04x img %u",
922 			ctx->addr, idx);
923 		if (cli->req.type != REQ_METADATA) {
924 			LOG_WRN("Expected %u", cli->req.type);
925 		} else {
926 			LOG_WRN("Expected 0x%04x img %u", cli->req.addr, idx);
927 		}
928 
929 		return 0;
930 	}
931 
932 	rsp->status = hdr & BIT_MASK(3);
933 	rsp->effect = (hdr >> 3);
934 	k_sem_give(&cli->req.sem);
935 
936 	return 0;
937 }
938 
939 const struct bt_mesh_model_op _bt_mesh_dfu_cli_op[] = {
940 	{BT_MESH_DFU_OP_UPDATE_STATUS, BT_MESH_LEN_MIN(1), handle_status},
941 	{BT_MESH_DFU_OP_UPDATE_INFO_STATUS, BT_MESH_LEN_MIN(2), handle_info_status},
942 	{BT_MESH_DFU_OP_UPDATE_METADATA_STATUS, BT_MESH_LEN_EXACT(2), handle_metadata_status},
943 	BT_MESH_MODEL_OP_END,
944 };
945 
dfu_cli_init(struct bt_mesh_model * mod)946 static int dfu_cli_init(struct bt_mesh_model *mod)
947 {
948 	struct bt_mesh_dfu_cli *cli = mod->user_data;
949 
950 	if (mod->elem_idx != 0) {
951 		LOG_ERR("DFU update client must be instantiated on first elem");
952 		return -EINVAL;
953 	}
954 
955 	cli->mod = mod;
956 
957 	if (IS_ENABLED(CONFIG_BT_MESH_MODEL_EXTENSIONS)) {
958 		bt_mesh_model_extend(mod, cli->blob.mod);
959 	}
960 
961 	k_sem_init(&cli->req.sem, 0, 1);
962 
963 	return 0;
964 }
965 
dfu_cli_reset(struct bt_mesh_model * mod)966 static void dfu_cli_reset(struct bt_mesh_model *mod)
967 {
968 	struct bt_mesh_dfu_cli *cli = mod->user_data;
969 
970 	cli->req.type = REQ_NONE;
971 	cli->req.addr = BT_MESH_ADDR_UNASSIGNED;
972 	cli->req.img_cnt = 0;
973 	cli->req.img_cb = NULL;
974 	cli->xfer.state = STATE_IDLE;
975 	cli->xfer.flags = 0;
976 }
977 
978 const struct bt_mesh_model_cb _bt_mesh_dfu_cli_cb = {
979 	.init = dfu_cli_init,
980 	.reset = dfu_cli_reset,
981 };
982 
983 /*******************************************************************************
984  * Public API
985  ******************************************************************************/
986 
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)987 int bt_mesh_dfu_cli_send(struct bt_mesh_dfu_cli *cli,
988 			 const struct bt_mesh_blob_cli_inputs *inputs,
989 			 const struct bt_mesh_blob_io *io,
990 			 const struct bt_mesh_dfu_cli_xfer *xfer)
991 {
992 	struct bt_mesh_dfu_target *target;
993 
994 	if (bt_mesh_dfu_cli_is_busy(cli)) {
995 		return -EBUSY;
996 	}
997 
998 	cli->xfer.blob.mode = xfer->mode;
999 	cli->xfer.blob.size = xfer->slot->size;
1000 
1001 	if (xfer->blob_id == 0) {
1002 		sys_rand_get(&cli->xfer.blob.id, sizeof(cli->xfer.blob.id));
1003 	} else {
1004 		cli->xfer.blob.id = xfer->blob_id;
1005 	}
1006 
1007 	cli->xfer.io = io;
1008 	cli->blob.inputs = inputs;
1009 	cli->xfer.slot = xfer->slot;
1010 	cli->xfer.flags = 0U;
1011 
1012 	if (xfer->blob_params) {
1013 		cli->xfer.flags |= FLAG_SKIP_CAPS_GET;
1014 		cli->xfer.blob.block_size_log = xfer->blob_params->block_size_log;
1015 		cli->xfer.blob.chunk_size = xfer->blob_params->chunk_size;
1016 	}
1017 
1018 	/* Phase will be set based on target status messages: */
1019 	TARGETS_FOR_EACH(cli, target) {
1020 		target->status = BT_MESH_DFU_SUCCESS;
1021 		target->phase = BT_MESH_DFU_PHASE_UNKNOWN;
1022 	}
1023 
1024 	initiate(cli);
1025 	return 0;
1026 }
1027 
bt_mesh_dfu_cli_suspend(struct bt_mesh_dfu_cli * cli)1028 int bt_mesh_dfu_cli_suspend(struct bt_mesh_dfu_cli *cli)
1029 {
1030 	int err;
1031 
1032 	err = bt_mesh_blob_cli_suspend(&cli->blob);
1033 	if (!err) {
1034 		cli->xfer.state = STATE_SUSPENDED;
1035 	}
1036 
1037 	return err;
1038 }
1039 
bt_mesh_dfu_cli_resume(struct bt_mesh_dfu_cli * cli)1040 int bt_mesh_dfu_cli_resume(struct bt_mesh_dfu_cli *cli)
1041 {
1042 	struct bt_mesh_dfu_target *target;
1043 
1044 	if (cli->xfer.state != STATE_SUSPENDED) {
1045 		return -EINVAL;
1046 	}
1047 
1048 	cli->xfer.flags = FLAG_RESUME;
1049 
1050 	/* Restore timed out targets. */
1051 	TARGETS_FOR_EACH(cli, target) {
1052 		if (!!target->blob.timedout) {
1053 			target->status = BT_MESH_DFU_SUCCESS;
1054 			target->phase = BT_MESH_DFU_PHASE_UNKNOWN;
1055 		}
1056 	}
1057 
1058 	initiate(cli);
1059 	return 0;
1060 }
1061 
bt_mesh_dfu_cli_cancel(struct bt_mesh_dfu_cli * cli,struct bt_mesh_msg_ctx * ctx)1062 int bt_mesh_dfu_cli_cancel(struct bt_mesh_dfu_cli *cli,
1063 			   struct bt_mesh_msg_ctx *ctx)
1064 {
1065 	if (ctx) {
1066 		int err;
1067 
1068 		err = req_setup(cli, REQ_STATUS, ctx->addr, NULL);
1069 		if (err) {
1070 			return err;
1071 		}
1072 
1073 		BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_CANCEL, 0);
1074 		bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_CANCEL);
1075 
1076 		err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL);
1077 		if (err) {
1078 			cli->req.type = REQ_NONE;
1079 			return err;
1080 		}
1081 
1082 		return req_wait(cli, K_MSEC(dfu_cli_timeout));
1083 	}
1084 
1085 	if (cli->xfer.state == STATE_IDLE) {
1086 		return -EALREADY;
1087 	}
1088 
1089 	cli->xfer.state = STATE_CANCEL;
1090 	blob_cli_broadcast_abort(&cli->blob);
1091 	cancel(cli);
1092 	return 0;
1093 }
1094 
bt_mesh_dfu_cli_apply(struct bt_mesh_dfu_cli * cli)1095 int bt_mesh_dfu_cli_apply(struct bt_mesh_dfu_cli *cli)
1096 {
1097 	if (cli->xfer.state != STATE_VERIFIED) {
1098 		return -EBUSY;
1099 	}
1100 
1101 	apply(cli);
1102 
1103 	return 0;
1104 }
1105 
bt_mesh_dfu_cli_confirm(struct bt_mesh_dfu_cli * cli)1106 int bt_mesh_dfu_cli_confirm(struct bt_mesh_dfu_cli *cli)
1107 {
1108 	if (cli->xfer.state != STATE_APPLIED) {
1109 		return -EBUSY;
1110 	}
1111 
1112 	cli->xfer.state = STATE_CONFIRM;
1113 	confirm(cli);
1114 
1115 	return 0;
1116 }
1117 
bt_mesh_dfu_cli_progress(struct bt_mesh_dfu_cli * cli)1118 uint8_t bt_mesh_dfu_cli_progress(struct bt_mesh_dfu_cli *cli)
1119 {
1120 	if (cli->xfer.state == STATE_TRANSFER) {
1121 		return bt_mesh_blob_cli_xfer_progress_active_get(&cli->blob);
1122 	}
1123 
1124 	if (cli->xfer.state == STATE_IDLE) {
1125 		if (cli->xfer.flags & FLAG_COMPLETED) {
1126 			return 100U;
1127 		}
1128 		return 0U;
1129 	}
1130 
1131 	return 100U;
1132 }
1133 
bt_mesh_dfu_cli_is_busy(struct bt_mesh_dfu_cli * cli)1134 bool bt_mesh_dfu_cli_is_busy(struct bt_mesh_dfu_cli *cli)
1135 {
1136 	return (cli->xfer.state == STATE_TRANSFER ||
1137 		cli->xfer.state == STATE_REFRESH ||
1138 		cli->xfer.state == STATE_APPLY ||
1139 		cli->xfer.state == STATE_CONFIRM) &&
1140 	       !(cli->xfer.flags & FLAG_FAILED);
1141 }
1142 
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)1143 int bt_mesh_dfu_cli_imgs_get(struct bt_mesh_dfu_cli *cli,
1144 			     struct bt_mesh_msg_ctx *ctx,
1145 			     bt_mesh_dfu_img_cb_t cb, void *cb_data,
1146 			     uint8_t max_count)
1147 {
1148 	int err;
1149 
1150 	if (cli->req.img_cb) {
1151 		return -EBUSY;
1152 	}
1153 
1154 	err = req_setup(cli, REQ_IMG, ctx->addr, NULL);
1155 	if (err) {
1156 		return err;
1157 	}
1158 
1159 	cli->req.img_cb = cb;
1160 	cli->req.params = cb_data;
1161 	cli->req.ttl = ctx->send_ttl;
1162 	cli->req.img_cnt = max_count;
1163 
1164 	err = info_get(cli, ctx, 0, cli->req.img_cnt,  NULL);
1165 	if (err) {
1166 		cli->req.img_cb = NULL;
1167 		cli->req.type = REQ_NONE;
1168 		return err;
1169 	}
1170 
1171 	err = req_wait(cli, K_MSEC(dfu_cli_timeout));
1172 
1173 	cli->req.img_cb = NULL;
1174 
1175 	return err;
1176 }
1177 
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)1178 int bt_mesh_dfu_cli_metadata_check(struct bt_mesh_dfu_cli *cli,
1179 				   struct bt_mesh_msg_ctx *ctx, uint8_t img_idx,
1180 				   const struct bt_mesh_dfu_slot *slot,
1181 				   struct bt_mesh_dfu_metadata_status *rsp)
1182 {
1183 	int err;
1184 
1185 	err = req_setup(cli, REQ_METADATA, ctx->addr, rsp);
1186 	if (err) {
1187 		return err;
1188 	}
1189 
1190 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_METADATA_CHECK,
1191 				 1 + CONFIG_BT_MESH_DFU_METADATA_MAXLEN);
1192 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_METADATA_CHECK);
1193 
1194 	net_buf_simple_add_u8(&buf, img_idx);
1195 
1196 	if (slot->metadata_len) {
1197 		net_buf_simple_add_mem(&buf, slot->metadata,
1198 				       slot->metadata_len);
1199 	}
1200 
1201 	rsp->idx = img_idx;
1202 
1203 	err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL);
1204 	if (err) {
1205 		cli->req.type = REQ_NONE;
1206 		return err;
1207 	}
1208 
1209 	return req_wait(cli, K_MSEC(dfu_cli_timeout));
1210 }
1211 
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)1212 int bt_mesh_dfu_cli_status_get(struct bt_mesh_dfu_cli *cli,
1213 			       struct bt_mesh_msg_ctx *ctx,
1214 			       struct bt_mesh_dfu_target_status *rsp)
1215 {
1216 	int err;
1217 
1218 	err = req_setup(cli, REQ_STATUS, ctx->addr, rsp);
1219 	if (err) {
1220 		return err;
1221 	}
1222 
1223 	BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_GET, 0);
1224 	bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_GET);
1225 
1226 	err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL);
1227 	if (err) {
1228 		cli->req.type = REQ_NONE;
1229 		return err;
1230 	}
1231 
1232 	return req_wait(cli, K_MSEC(dfu_cli_timeout));
1233 }
1234 
bt_mesh_dfu_cli_timeout_get(void)1235 int32_t bt_mesh_dfu_cli_timeout_get(void)
1236 {
1237 	return dfu_cli_timeout;
1238 }
1239 
bt_mesh_dfu_cli_timeout_set(int32_t t)1240 void bt_mesh_dfu_cli_timeout_set(int32_t t)
1241 {
1242 	dfu_cli_timeout = t;
1243 }
1244