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