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